release(camera-ai): publish 0.4.12 catalog portability fix
This commit is contained in:
@@ -1,41 +1,41 @@
|
||||
from cython cimport floating
|
||||
|
||||
|
||||
cpdef enum BLAS_Order:
|
||||
RowMajor # C contiguous
|
||||
ColMajor # Fortran contiguous
|
||||
|
||||
|
||||
cpdef enum BLAS_Trans:
|
||||
NoTrans = 110 # correspond to 'n'
|
||||
Trans = 116 # correspond to 't'
|
||||
|
||||
|
||||
# BLAS Level 1 ################################################################
|
||||
cdef floating _dot(int, const floating*, int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef floating _asum(int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _axpy(int, floating, const floating*, int, floating*, int) noexcept nogil
|
||||
|
||||
cdef floating _nrm2(int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _copy(int, const floating*, int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _scal(int, floating, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _rotg(floating*, floating*, floating*, floating*) noexcept nogil
|
||||
|
||||
cdef void _rot(int, floating*, int, floating*, int, floating, floating) noexcept nogil
|
||||
|
||||
# BLAS Level 2 ################################################################
|
||||
cdef void _gemv(BLAS_Order, BLAS_Trans, int, int, floating, const floating*, int,
|
||||
const floating*, int, floating, floating*, int) noexcept nogil
|
||||
|
||||
cdef void _ger(BLAS_Order, int, int, floating, const floating*, int, const floating*,
|
||||
int, floating*, int) noexcept nogil
|
||||
|
||||
# BLASLevel 3 ################################################################
|
||||
cdef void _gemm(BLAS_Order, BLAS_Trans, BLAS_Trans, int, int, int, floating,
|
||||
const floating*, int, const floating*, int, floating, floating*,
|
||||
int) noexcept nogil
|
||||
from cython cimport floating
|
||||
|
||||
|
||||
cpdef enum BLAS_Order:
|
||||
RowMajor # C contiguous
|
||||
ColMajor # Fortran contiguous
|
||||
|
||||
|
||||
cpdef enum BLAS_Trans:
|
||||
NoTrans = 110 # correspond to 'n'
|
||||
Trans = 116 # correspond to 't'
|
||||
|
||||
|
||||
# BLAS Level 1 ################################################################
|
||||
cdef floating _dot(int, const floating*, int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef floating _asum(int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _axpy(int, floating, const floating*, int, floating*, int) noexcept nogil
|
||||
|
||||
cdef floating _nrm2(int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _copy(int, const floating*, int, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _scal(int, floating, const floating*, int) noexcept nogil
|
||||
|
||||
cdef void _rotg(floating*, floating*, floating*, floating*) noexcept nogil
|
||||
|
||||
cdef void _rot(int, floating*, int, floating*, int, floating, floating) noexcept nogil
|
||||
|
||||
# BLAS Level 2 ################################################################
|
||||
cdef void _gemv(BLAS_Order, BLAS_Trans, int, int, floating, const floating*, int,
|
||||
const floating*, int, floating, floating*, int) noexcept nogil
|
||||
|
||||
cdef void _ger(BLAS_Order, int, int, floating, const floating*, int, const floating*,
|
||||
int, floating*, int) noexcept nogil
|
||||
|
||||
# BLASLevel 3 ################################################################
|
||||
cdef void _gemm(BLAS_Order, BLAS_Trans, BLAS_Trans, int, int, int, floating,
|
||||
const floating*, int, const floating*, int, floating, floating*,
|
||||
int) noexcept nogil
|
||||
|
||||
@@ -1,239 +1,239 @@
|
||||
from cython cimport floating
|
||||
|
||||
from scipy.linalg.cython_blas cimport sdot, ddot
|
||||
from scipy.linalg.cython_blas cimport sasum, dasum
|
||||
from scipy.linalg.cython_blas cimport saxpy, daxpy
|
||||
from scipy.linalg.cython_blas cimport snrm2, dnrm2
|
||||
from scipy.linalg.cython_blas cimport scopy, dcopy
|
||||
from scipy.linalg.cython_blas cimport sscal, dscal
|
||||
from scipy.linalg.cython_blas cimport srotg, drotg
|
||||
from scipy.linalg.cython_blas cimport srot, drot
|
||||
from scipy.linalg.cython_blas cimport sgemv, dgemv
|
||||
from scipy.linalg.cython_blas cimport sger, dger
|
||||
from scipy.linalg.cython_blas cimport sgemm, dgemm
|
||||
|
||||
|
||||
################
|
||||
# BLAS Level 1 #
|
||||
################
|
||||
|
||||
cdef floating _dot(int n, const floating *x, int incx,
|
||||
const floating *y, int incy) noexcept nogil:
|
||||
"""x.T.y"""
|
||||
if floating is float:
|
||||
return sdot(&n, <float *> x, &incx, <float *> y, &incy)
|
||||
else:
|
||||
return ddot(&n, <double *> x, &incx, <double *> y, &incy)
|
||||
|
||||
|
||||
cpdef _dot_memview(const floating[::1] x, const floating[::1] y):
|
||||
return _dot(x.shape[0], &x[0], 1, &y[0], 1)
|
||||
|
||||
|
||||
cdef floating _asum(int n, const floating *x, int incx) noexcept nogil:
|
||||
"""sum(|x_i|)"""
|
||||
if floating is float:
|
||||
return sasum(&n, <float *> x, &incx)
|
||||
else:
|
||||
return dasum(&n, <double *> x, &incx)
|
||||
|
||||
|
||||
cpdef _asum_memview(const floating[::1] x):
|
||||
return _asum(x.shape[0], &x[0], 1)
|
||||
|
||||
|
||||
cdef void _axpy(int n, floating alpha, const floating *x, int incx,
|
||||
floating *y, int incy) noexcept nogil:
|
||||
"""y := alpha * x + y"""
|
||||
if floating is float:
|
||||
saxpy(&n, &alpha, <float *> x, &incx, y, &incy)
|
||||
else:
|
||||
daxpy(&n, &alpha, <double *> x, &incx, y, &incy)
|
||||
|
||||
|
||||
cpdef _axpy_memview(floating alpha, const floating[::1] x, floating[::1] y):
|
||||
_axpy(x.shape[0], alpha, &x[0], 1, &y[0], 1)
|
||||
|
||||
|
||||
cdef floating _nrm2(int n, const floating *x, int incx) noexcept nogil:
|
||||
"""sqrt(sum((x_i)^2))"""
|
||||
if floating is float:
|
||||
return snrm2(&n, <float *> x, &incx)
|
||||
else:
|
||||
return dnrm2(&n, <double *> x, &incx)
|
||||
|
||||
|
||||
cpdef _nrm2_memview(const floating[::1] x):
|
||||
return _nrm2(x.shape[0], &x[0], 1)
|
||||
|
||||
|
||||
cdef void _copy(int n, const floating *x, int incx, const floating *y, int incy) noexcept nogil:
|
||||
"""y := x"""
|
||||
if floating is float:
|
||||
scopy(&n, <float *> x, &incx, <float *> y, &incy)
|
||||
else:
|
||||
dcopy(&n, <double *> x, &incx, <double *> y, &incy)
|
||||
|
||||
|
||||
cpdef _copy_memview(const floating[::1] x, const floating[::1] y):
|
||||
_copy(x.shape[0], &x[0], 1, &y[0], 1)
|
||||
|
||||
|
||||
cdef void _scal(int n, floating alpha, const floating *x, int incx) noexcept nogil:
|
||||
"""x := alpha * x"""
|
||||
if floating is float:
|
||||
sscal(&n, &alpha, <float *> x, &incx)
|
||||
else:
|
||||
dscal(&n, &alpha, <double *> x, &incx)
|
||||
|
||||
|
||||
cpdef _scal_memview(floating alpha, const floating[::1] x):
|
||||
_scal(x.shape[0], alpha, &x[0], 1)
|
||||
|
||||
|
||||
cdef void _rotg(floating *a, floating *b, floating *c, floating *s) noexcept nogil:
|
||||
"""Generate plane rotation"""
|
||||
if floating is float:
|
||||
srotg(a, b, c, s)
|
||||
else:
|
||||
drotg(a, b, c, s)
|
||||
|
||||
|
||||
cpdef _rotg_memview(floating a, floating b, floating c, floating s):
|
||||
_rotg(&a, &b, &c, &s)
|
||||
return a, b, c, s
|
||||
|
||||
|
||||
cdef void _rot(int n, floating *x, int incx, floating *y, int incy,
|
||||
floating c, floating s) noexcept nogil:
|
||||
"""Apply plane rotation"""
|
||||
if floating is float:
|
||||
srot(&n, x, &incx, y, &incy, &c, &s)
|
||||
else:
|
||||
drot(&n, x, &incx, y, &incy, &c, &s)
|
||||
|
||||
|
||||
cpdef _rot_memview(floating[::1] x, floating[::1] y, floating c, floating s):
|
||||
_rot(x.shape[0], &x[0], 1, &y[0], 1, c, s)
|
||||
|
||||
|
||||
################
|
||||
# BLAS Level 2 #
|
||||
################
|
||||
|
||||
cdef void _gemv(BLAS_Order order, BLAS_Trans ta, int m, int n, floating alpha,
|
||||
const floating *A, int lda, const floating *x, int incx,
|
||||
floating beta, floating *y, int incy) noexcept nogil:
|
||||
"""y := alpha * op(A).x + beta * y"""
|
||||
cdef char ta_ = ta
|
||||
if order == BLAS_Order.RowMajor:
|
||||
ta_ = BLAS_Trans.NoTrans if ta == BLAS_Trans.Trans else BLAS_Trans.Trans
|
||||
if floating is float:
|
||||
sgemv(&ta_, &n, &m, &alpha, <float *> A, &lda, <float *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
else:
|
||||
dgemv(&ta_, &n, &m, &alpha, <double *> A, &lda, <double *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
else:
|
||||
if floating is float:
|
||||
sgemv(&ta_, &m, &n, &alpha, <float *> A, &lda, <float *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
else:
|
||||
dgemv(&ta_, &m, &n, &alpha, <double *> A, &lda, <double *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
|
||||
|
||||
cpdef _gemv_memview(BLAS_Trans ta, floating alpha, const floating[:, :] A,
|
||||
const floating[::1] x, floating beta, floating[::1] y):
|
||||
cdef:
|
||||
int m = A.shape[0]
|
||||
int n = A.shape[1]
|
||||
BLAS_Order order = (
|
||||
BLAS_Order.ColMajor if A.strides[0] == A.itemsize else BLAS_Order.RowMajor
|
||||
)
|
||||
int lda = m if order == BLAS_Order.ColMajor else n
|
||||
|
||||
_gemv(order, ta, m, n, alpha, &A[0, 0], lda, &x[0], 1, beta, &y[0], 1)
|
||||
|
||||
|
||||
cdef void _ger(BLAS_Order order, int m, int n, floating alpha,
|
||||
const floating *x, int incx, const floating *y,
|
||||
int incy, floating *A, int lda) noexcept nogil:
|
||||
"""A := alpha * x.y.T + A"""
|
||||
if order == BLAS_Order.RowMajor:
|
||||
if floating is float:
|
||||
sger(&n, &m, &alpha, <float *> y, &incy, <float *> x, &incx, A, &lda)
|
||||
else:
|
||||
dger(&n, &m, &alpha, <double *> y, &incy, <double *> x, &incx, A, &lda)
|
||||
else:
|
||||
if floating is float:
|
||||
sger(&m, &n, &alpha, <float *> x, &incx, <float *> y, &incy, A, &lda)
|
||||
else:
|
||||
dger(&m, &n, &alpha, <double *> x, &incx, <double *> y, &incy, A, &lda)
|
||||
|
||||
|
||||
cpdef _ger_memview(floating alpha, const floating[::1] x,
|
||||
const floating[::1] y, floating[:, :] A):
|
||||
cdef:
|
||||
int m = A.shape[0]
|
||||
int n = A.shape[1]
|
||||
BLAS_Order order = (
|
||||
BLAS_Order.ColMajor if A.strides[0] == A.itemsize else BLAS_Order.RowMajor
|
||||
)
|
||||
int lda = m if order == BLAS_Order.ColMajor else n
|
||||
|
||||
_ger(order, m, n, alpha, &x[0], 1, &y[0], 1, &A[0, 0], lda)
|
||||
|
||||
|
||||
################
|
||||
# BLAS Level 3 #
|
||||
################
|
||||
|
||||
cdef void _gemm(BLAS_Order order, BLAS_Trans ta, BLAS_Trans tb, int m, int n,
|
||||
int k, floating alpha, const floating *A, int lda, const floating *B,
|
||||
int ldb, floating beta, floating *C, int ldc) noexcept nogil:
|
||||
"""C := alpha * op(A).op(B) + beta * C"""
|
||||
# TODO: Remove the pointer casts below once SciPy uses const-qualification.
|
||||
# See: https://github.com/scipy/scipy/issues/14262
|
||||
cdef:
|
||||
char ta_ = ta
|
||||
char tb_ = tb
|
||||
if order == BLAS_Order.RowMajor:
|
||||
if floating is float:
|
||||
sgemm(&tb_, &ta_, &n, &m, &k, &alpha, <float*>B,
|
||||
&ldb, <float*>A, &lda, &beta, C, &ldc)
|
||||
else:
|
||||
dgemm(&tb_, &ta_, &n, &m, &k, &alpha, <double*>B,
|
||||
&ldb, <double*>A, &lda, &beta, C, &ldc)
|
||||
else:
|
||||
if floating is float:
|
||||
sgemm(&ta_, &tb_, &m, &n, &k, &alpha, <float*>A,
|
||||
&lda, <float*>B, &ldb, &beta, C, &ldc)
|
||||
else:
|
||||
dgemm(&ta_, &tb_, &m, &n, &k, &alpha, <double*>A,
|
||||
&lda, <double*>B, &ldb, &beta, C, &ldc)
|
||||
|
||||
|
||||
cpdef _gemm_memview(BLAS_Trans ta, BLAS_Trans tb, floating alpha,
|
||||
const floating[:, :] A, const floating[:, :] B, floating beta,
|
||||
floating[:, :] C):
|
||||
cdef:
|
||||
int m = A.shape[0] if ta == BLAS_Trans.NoTrans else A.shape[1]
|
||||
int n = B.shape[1] if tb == BLAS_Trans.NoTrans else B.shape[0]
|
||||
int k = A.shape[1] if ta == BLAS_Trans.NoTrans else A.shape[0]
|
||||
int lda, ldb, ldc
|
||||
BLAS_Order order = (
|
||||
BLAS_Order.ColMajor if A.strides[0] == A.itemsize else BLAS_Order.RowMajor
|
||||
)
|
||||
|
||||
if order == BLAS_Order.RowMajor:
|
||||
lda = k if ta == BLAS_Trans.NoTrans else m
|
||||
ldb = n if tb == BLAS_Trans.NoTrans else k
|
||||
ldc = n
|
||||
else:
|
||||
lda = m if ta == BLAS_Trans.NoTrans else k
|
||||
ldb = k if tb == BLAS_Trans.NoTrans else n
|
||||
ldc = m
|
||||
|
||||
_gemm(order, ta, tb, m, n, k, alpha, &A[0, 0],
|
||||
lda, &B[0, 0], ldb, beta, &C[0, 0], ldc)
|
||||
from cython cimport floating
|
||||
|
||||
from scipy.linalg.cython_blas cimport sdot, ddot
|
||||
from scipy.linalg.cython_blas cimport sasum, dasum
|
||||
from scipy.linalg.cython_blas cimport saxpy, daxpy
|
||||
from scipy.linalg.cython_blas cimport snrm2, dnrm2
|
||||
from scipy.linalg.cython_blas cimport scopy, dcopy
|
||||
from scipy.linalg.cython_blas cimport sscal, dscal
|
||||
from scipy.linalg.cython_blas cimport srotg, drotg
|
||||
from scipy.linalg.cython_blas cimport srot, drot
|
||||
from scipy.linalg.cython_blas cimport sgemv, dgemv
|
||||
from scipy.linalg.cython_blas cimport sger, dger
|
||||
from scipy.linalg.cython_blas cimport sgemm, dgemm
|
||||
|
||||
|
||||
################
|
||||
# BLAS Level 1 #
|
||||
################
|
||||
|
||||
cdef floating _dot(int n, const floating *x, int incx,
|
||||
const floating *y, int incy) noexcept nogil:
|
||||
"""x.T.y"""
|
||||
if floating is float:
|
||||
return sdot(&n, <float *> x, &incx, <float *> y, &incy)
|
||||
else:
|
||||
return ddot(&n, <double *> x, &incx, <double *> y, &incy)
|
||||
|
||||
|
||||
cpdef _dot_memview(const floating[::1] x, const floating[::1] y):
|
||||
return _dot(x.shape[0], &x[0], 1, &y[0], 1)
|
||||
|
||||
|
||||
cdef floating _asum(int n, const floating *x, int incx) noexcept nogil:
|
||||
"""sum(|x_i|)"""
|
||||
if floating is float:
|
||||
return sasum(&n, <float *> x, &incx)
|
||||
else:
|
||||
return dasum(&n, <double *> x, &incx)
|
||||
|
||||
|
||||
cpdef _asum_memview(const floating[::1] x):
|
||||
return _asum(x.shape[0], &x[0], 1)
|
||||
|
||||
|
||||
cdef void _axpy(int n, floating alpha, const floating *x, int incx,
|
||||
floating *y, int incy) noexcept nogil:
|
||||
"""y := alpha * x + y"""
|
||||
if floating is float:
|
||||
saxpy(&n, &alpha, <float *> x, &incx, y, &incy)
|
||||
else:
|
||||
daxpy(&n, &alpha, <double *> x, &incx, y, &incy)
|
||||
|
||||
|
||||
cpdef _axpy_memview(floating alpha, const floating[::1] x, floating[::1] y):
|
||||
_axpy(x.shape[0], alpha, &x[0], 1, &y[0], 1)
|
||||
|
||||
|
||||
cdef floating _nrm2(int n, const floating *x, int incx) noexcept nogil:
|
||||
"""sqrt(sum((x_i)^2))"""
|
||||
if floating is float:
|
||||
return snrm2(&n, <float *> x, &incx)
|
||||
else:
|
||||
return dnrm2(&n, <double *> x, &incx)
|
||||
|
||||
|
||||
cpdef _nrm2_memview(const floating[::1] x):
|
||||
return _nrm2(x.shape[0], &x[0], 1)
|
||||
|
||||
|
||||
cdef void _copy(int n, const floating *x, int incx, const floating *y, int incy) noexcept nogil:
|
||||
"""y := x"""
|
||||
if floating is float:
|
||||
scopy(&n, <float *> x, &incx, <float *> y, &incy)
|
||||
else:
|
||||
dcopy(&n, <double *> x, &incx, <double *> y, &incy)
|
||||
|
||||
|
||||
cpdef _copy_memview(const floating[::1] x, const floating[::1] y):
|
||||
_copy(x.shape[0], &x[0], 1, &y[0], 1)
|
||||
|
||||
|
||||
cdef void _scal(int n, floating alpha, const floating *x, int incx) noexcept nogil:
|
||||
"""x := alpha * x"""
|
||||
if floating is float:
|
||||
sscal(&n, &alpha, <float *> x, &incx)
|
||||
else:
|
||||
dscal(&n, &alpha, <double *> x, &incx)
|
||||
|
||||
|
||||
cpdef _scal_memview(floating alpha, const floating[::1] x):
|
||||
_scal(x.shape[0], alpha, &x[0], 1)
|
||||
|
||||
|
||||
cdef void _rotg(floating *a, floating *b, floating *c, floating *s) noexcept nogil:
|
||||
"""Generate plane rotation"""
|
||||
if floating is float:
|
||||
srotg(a, b, c, s)
|
||||
else:
|
||||
drotg(a, b, c, s)
|
||||
|
||||
|
||||
cpdef _rotg_memview(floating a, floating b, floating c, floating s):
|
||||
_rotg(&a, &b, &c, &s)
|
||||
return a, b, c, s
|
||||
|
||||
|
||||
cdef void _rot(int n, floating *x, int incx, floating *y, int incy,
|
||||
floating c, floating s) noexcept nogil:
|
||||
"""Apply plane rotation"""
|
||||
if floating is float:
|
||||
srot(&n, x, &incx, y, &incy, &c, &s)
|
||||
else:
|
||||
drot(&n, x, &incx, y, &incy, &c, &s)
|
||||
|
||||
|
||||
cpdef _rot_memview(floating[::1] x, floating[::1] y, floating c, floating s):
|
||||
_rot(x.shape[0], &x[0], 1, &y[0], 1, c, s)
|
||||
|
||||
|
||||
################
|
||||
# BLAS Level 2 #
|
||||
################
|
||||
|
||||
cdef void _gemv(BLAS_Order order, BLAS_Trans ta, int m, int n, floating alpha,
|
||||
const floating *A, int lda, const floating *x, int incx,
|
||||
floating beta, floating *y, int incy) noexcept nogil:
|
||||
"""y := alpha * op(A).x + beta * y"""
|
||||
cdef char ta_ = ta
|
||||
if order == BLAS_Order.RowMajor:
|
||||
ta_ = BLAS_Trans.NoTrans if ta == BLAS_Trans.Trans else BLAS_Trans.Trans
|
||||
if floating is float:
|
||||
sgemv(&ta_, &n, &m, &alpha, <float *> A, &lda, <float *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
else:
|
||||
dgemv(&ta_, &n, &m, &alpha, <double *> A, &lda, <double *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
else:
|
||||
if floating is float:
|
||||
sgemv(&ta_, &m, &n, &alpha, <float *> A, &lda, <float *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
else:
|
||||
dgemv(&ta_, &m, &n, &alpha, <double *> A, &lda, <double *> x,
|
||||
&incx, &beta, y, &incy)
|
||||
|
||||
|
||||
cpdef _gemv_memview(BLAS_Trans ta, floating alpha, const floating[:, :] A,
|
||||
const floating[::1] x, floating beta, floating[::1] y):
|
||||
cdef:
|
||||
int m = A.shape[0]
|
||||
int n = A.shape[1]
|
||||
BLAS_Order order = (
|
||||
BLAS_Order.ColMajor if A.strides[0] == A.itemsize else BLAS_Order.RowMajor
|
||||
)
|
||||
int lda = m if order == BLAS_Order.ColMajor else n
|
||||
|
||||
_gemv(order, ta, m, n, alpha, &A[0, 0], lda, &x[0], 1, beta, &y[0], 1)
|
||||
|
||||
|
||||
cdef void _ger(BLAS_Order order, int m, int n, floating alpha,
|
||||
const floating *x, int incx, const floating *y,
|
||||
int incy, floating *A, int lda) noexcept nogil:
|
||||
"""A := alpha * x.y.T + A"""
|
||||
if order == BLAS_Order.RowMajor:
|
||||
if floating is float:
|
||||
sger(&n, &m, &alpha, <float *> y, &incy, <float *> x, &incx, A, &lda)
|
||||
else:
|
||||
dger(&n, &m, &alpha, <double *> y, &incy, <double *> x, &incx, A, &lda)
|
||||
else:
|
||||
if floating is float:
|
||||
sger(&m, &n, &alpha, <float *> x, &incx, <float *> y, &incy, A, &lda)
|
||||
else:
|
||||
dger(&m, &n, &alpha, <double *> x, &incx, <double *> y, &incy, A, &lda)
|
||||
|
||||
|
||||
cpdef _ger_memview(floating alpha, const floating[::1] x,
|
||||
const floating[::1] y, floating[:, :] A):
|
||||
cdef:
|
||||
int m = A.shape[0]
|
||||
int n = A.shape[1]
|
||||
BLAS_Order order = (
|
||||
BLAS_Order.ColMajor if A.strides[0] == A.itemsize else BLAS_Order.RowMajor
|
||||
)
|
||||
int lda = m if order == BLAS_Order.ColMajor else n
|
||||
|
||||
_ger(order, m, n, alpha, &x[0], 1, &y[0], 1, &A[0, 0], lda)
|
||||
|
||||
|
||||
################
|
||||
# BLAS Level 3 #
|
||||
################
|
||||
|
||||
cdef void _gemm(BLAS_Order order, BLAS_Trans ta, BLAS_Trans tb, int m, int n,
|
||||
int k, floating alpha, const floating *A, int lda, const floating *B,
|
||||
int ldb, floating beta, floating *C, int ldc) noexcept nogil:
|
||||
"""C := alpha * op(A).op(B) + beta * C"""
|
||||
# TODO: Remove the pointer casts below once SciPy uses const-qualification.
|
||||
# See: https://github.com/scipy/scipy/issues/14262
|
||||
cdef:
|
||||
char ta_ = ta
|
||||
char tb_ = tb
|
||||
if order == BLAS_Order.RowMajor:
|
||||
if floating is float:
|
||||
sgemm(&tb_, &ta_, &n, &m, &k, &alpha, <float*>B,
|
||||
&ldb, <float*>A, &lda, &beta, C, &ldc)
|
||||
else:
|
||||
dgemm(&tb_, &ta_, &n, &m, &k, &alpha, <double*>B,
|
||||
&ldb, <double*>A, &lda, &beta, C, &ldc)
|
||||
else:
|
||||
if floating is float:
|
||||
sgemm(&ta_, &tb_, &m, &n, &k, &alpha, <float*>A,
|
||||
&lda, <float*>B, &ldb, &beta, C, &ldc)
|
||||
else:
|
||||
dgemm(&ta_, &tb_, &m, &n, &k, &alpha, <double*>A,
|
||||
&lda, <double*>B, &ldb, &beta, C, &ldc)
|
||||
|
||||
|
||||
cpdef _gemm_memview(BLAS_Trans ta, BLAS_Trans tb, floating alpha,
|
||||
const floating[:, :] A, const floating[:, :] B, floating beta,
|
||||
floating[:, :] C):
|
||||
cdef:
|
||||
int m = A.shape[0] if ta == BLAS_Trans.NoTrans else A.shape[1]
|
||||
int n = B.shape[1] if tb == BLAS_Trans.NoTrans else B.shape[0]
|
||||
int k = A.shape[1] if ta == BLAS_Trans.NoTrans else A.shape[0]
|
||||
int lda, ldb, ldc
|
||||
BLAS_Order order = (
|
||||
BLAS_Order.ColMajor if A.strides[0] == A.itemsize else BLAS_Order.RowMajor
|
||||
)
|
||||
|
||||
if order == BLAS_Order.RowMajor:
|
||||
lda = k if ta == BLAS_Trans.NoTrans else m
|
||||
ldb = n if tb == BLAS_Trans.NoTrans else k
|
||||
ldc = n
|
||||
else:
|
||||
lda = m if ta == BLAS_Trans.NoTrans else k
|
||||
ldb = k if tb == BLAS_Trans.NoTrans else n
|
||||
ldc = m
|
||||
|
||||
_gemm(order, ta, tb, m, n, k, alpha, &A[0, 0],
|
||||
lda, &B[0, 0], ldb, beta, &C[0, 0], ldc)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""
|
||||
Uses C++ map containers for fast dict-like behavior with keys being
|
||||
integers, and values float.
|
||||
"""
|
||||
|
||||
from libcpp.map cimport map as cpp_map
|
||||
|
||||
from ._typedefs cimport float64_t, intp_t
|
||||
|
||||
|
||||
###############################################################################
|
||||
# An object to be used in Python
|
||||
|
||||
cdef class IntFloatDict:
|
||||
cdef cpp_map[intp_t, float64_t] my_map
|
||||
cdef _to_arrays(self, intp_t [:] keys, float64_t [:] values)
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""
|
||||
Uses C++ map containers for fast dict-like behavior with keys being
|
||||
integers, and values float.
|
||||
"""
|
||||
|
||||
from libcpp.map cimport map as cpp_map
|
||||
|
||||
from ._typedefs cimport float64_t, intp_t
|
||||
|
||||
|
||||
###############################################################################
|
||||
# An object to be used in Python
|
||||
|
||||
cdef class IntFloatDict:
|
||||
cdef cpp_map[intp_t, float64_t] my_map
|
||||
cdef _to_arrays(self, intp_t [:] keys, float64_t [:] values)
|
||||
|
||||
@@ -1,137 +1,137 @@
|
||||
"""
|
||||
Uses C++ map containers for fast dict-like behavior with keys being
|
||||
integers, and values float.
|
||||
"""
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# C++
|
||||
from cython.operator cimport dereference as deref, preincrement as inc
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.map cimport map as cpp_map
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ._typedefs cimport float64_t, intp_t
|
||||
|
||||
|
||||
###############################################################################
|
||||
# An object to be used in Python
|
||||
|
||||
# Lookup is faster than dict (up to 10 times), and so is full traversal
|
||||
# (up to 50 times), and assignment (up to 6 times), but creation is
|
||||
# slower (up to 3 times). Also, a large benefit is that memory
|
||||
# consumption is reduced a lot compared to a Python dict
|
||||
|
||||
cdef class IntFloatDict:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
intp_t[:] keys,
|
||||
float64_t[:] values,
|
||||
):
|
||||
cdef int i
|
||||
cdef int size = values.size
|
||||
# Should check that sizes for keys and values are equal, and
|
||||
# after should boundcheck(False)
|
||||
for i in range(size):
|
||||
self.my_map[keys[i]] = values[i]
|
||||
|
||||
def __len__(self):
|
||||
return self.my_map.size()
|
||||
|
||||
def __getitem__(self, int key):
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = self.my_map.find(key)
|
||||
if it == self.my_map.end():
|
||||
# The key is not in the dict
|
||||
raise KeyError('%i' % key)
|
||||
return deref(it).second
|
||||
|
||||
def __setitem__(self, int key, float value):
|
||||
self.my_map[key] = value
|
||||
|
||||
# Cython 0.20 generates buggy code below. Commenting this out for now
|
||||
# and relying on the to_arrays method
|
||||
# def __iter__(self):
|
||||
# cdef cpp_map[intp_t, float64_t].iterator it = self.my_map.begin()
|
||||
# cdef cpp_map[intp_t, float64_t].iterator end = self.my_map.end()
|
||||
# while it != end:
|
||||
# yield deref(it).first, deref(it).second
|
||||
# inc(it)
|
||||
|
||||
def __iter__(self):
|
||||
cdef int size = self.my_map.size()
|
||||
cdef intp_t [:] keys = np.empty(size, dtype=np.intp)
|
||||
cdef float64_t [:] values = np.empty(size, dtype=np.float64)
|
||||
self._to_arrays(keys, values)
|
||||
cdef int idx
|
||||
cdef intp_t key
|
||||
cdef float64_t value
|
||||
for idx in range(size):
|
||||
key = keys[idx]
|
||||
value = values[idx]
|
||||
yield key, value
|
||||
|
||||
def to_arrays(self):
|
||||
"""Return the key, value representation of the IntFloatDict
|
||||
object.
|
||||
|
||||
Returns
|
||||
=======
|
||||
keys : ndarray, shape (n_items, ), dtype=int
|
||||
The indices of the data points
|
||||
values : ndarray, shape (n_items, ), dtype=float
|
||||
The values of the data points
|
||||
"""
|
||||
cdef int size = self.my_map.size()
|
||||
keys = np.empty(size, dtype=np.intp)
|
||||
values = np.empty(size, dtype=np.float64)
|
||||
self._to_arrays(keys, values)
|
||||
return keys, values
|
||||
|
||||
cdef _to_arrays(self, intp_t [:] keys, float64_t [:] values):
|
||||
# Internal version of to_arrays that takes already-initialized arrays
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = self.my_map.begin()
|
||||
cdef cpp_map[intp_t, float64_t].iterator end = self.my_map.end()
|
||||
cdef int index = 0
|
||||
while it != end:
|
||||
keys[index] = deref(it).first
|
||||
values[index] = deref(it).second
|
||||
inc(it)
|
||||
index += 1
|
||||
|
||||
def update(self, IntFloatDict other):
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = other.my_map.begin()
|
||||
cdef cpp_map[intp_t, float64_t].iterator end = other.my_map.end()
|
||||
while it != end:
|
||||
self.my_map[deref(it).first] = deref(it).second
|
||||
inc(it)
|
||||
|
||||
def copy(self):
|
||||
cdef IntFloatDict out_obj = IntFloatDict.__new__(IntFloatDict)
|
||||
# The '=' operator is a copy operator for C++ maps
|
||||
out_obj.my_map = self.my_map
|
||||
return out_obj
|
||||
|
||||
def append(self, intp_t key, float64_t value):
|
||||
# Construct our arguments
|
||||
cdef pair[intp_t, float64_t] args
|
||||
args.first = key
|
||||
args.second = value
|
||||
self.my_map.insert(args)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# operation on dict
|
||||
|
||||
def argmin(IntFloatDict d):
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = d.my_map.begin()
|
||||
cdef cpp_map[intp_t, float64_t].iterator end = d.my_map.end()
|
||||
cdef intp_t min_key = -1
|
||||
cdef float64_t min_value = np.inf
|
||||
while it != end:
|
||||
if deref(it).second < min_value:
|
||||
min_value = deref(it).second
|
||||
min_key = deref(it).first
|
||||
inc(it)
|
||||
return min_key, min_value
|
||||
"""
|
||||
Uses C++ map containers for fast dict-like behavior with keys being
|
||||
integers, and values float.
|
||||
"""
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# C++
|
||||
from cython.operator cimport dereference as deref, preincrement as inc
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.map cimport map as cpp_map
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ._typedefs cimport float64_t, intp_t
|
||||
|
||||
|
||||
###############################################################################
|
||||
# An object to be used in Python
|
||||
|
||||
# Lookup is faster than dict (up to 10 times), and so is full traversal
|
||||
# (up to 50 times), and assignment (up to 6 times), but creation is
|
||||
# slower (up to 3 times). Also, a large benefit is that memory
|
||||
# consumption is reduced a lot compared to a Python dict
|
||||
|
||||
cdef class IntFloatDict:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
intp_t[:] keys,
|
||||
float64_t[:] values,
|
||||
):
|
||||
cdef int i
|
||||
cdef int size = values.size
|
||||
# Should check that sizes for keys and values are equal, and
|
||||
# after should boundcheck(False)
|
||||
for i in range(size):
|
||||
self.my_map[keys[i]] = values[i]
|
||||
|
||||
def __len__(self):
|
||||
return self.my_map.size()
|
||||
|
||||
def __getitem__(self, int key):
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = self.my_map.find(key)
|
||||
if it == self.my_map.end():
|
||||
# The key is not in the dict
|
||||
raise KeyError('%i' % key)
|
||||
return deref(it).second
|
||||
|
||||
def __setitem__(self, int key, float value):
|
||||
self.my_map[key] = value
|
||||
|
||||
# Cython 0.20 generates buggy code below. Commenting this out for now
|
||||
# and relying on the to_arrays method
|
||||
# def __iter__(self):
|
||||
# cdef cpp_map[intp_t, float64_t].iterator it = self.my_map.begin()
|
||||
# cdef cpp_map[intp_t, float64_t].iterator end = self.my_map.end()
|
||||
# while it != end:
|
||||
# yield deref(it).first, deref(it).second
|
||||
# inc(it)
|
||||
|
||||
def __iter__(self):
|
||||
cdef int size = self.my_map.size()
|
||||
cdef intp_t [:] keys = np.empty(size, dtype=np.intp)
|
||||
cdef float64_t [:] values = np.empty(size, dtype=np.float64)
|
||||
self._to_arrays(keys, values)
|
||||
cdef int idx
|
||||
cdef intp_t key
|
||||
cdef float64_t value
|
||||
for idx in range(size):
|
||||
key = keys[idx]
|
||||
value = values[idx]
|
||||
yield key, value
|
||||
|
||||
def to_arrays(self):
|
||||
"""Return the key, value representation of the IntFloatDict
|
||||
object.
|
||||
|
||||
Returns
|
||||
=======
|
||||
keys : ndarray, shape (n_items, ), dtype=int
|
||||
The indices of the data points
|
||||
values : ndarray, shape (n_items, ), dtype=float
|
||||
The values of the data points
|
||||
"""
|
||||
cdef int size = self.my_map.size()
|
||||
keys = np.empty(size, dtype=np.intp)
|
||||
values = np.empty(size, dtype=np.float64)
|
||||
self._to_arrays(keys, values)
|
||||
return keys, values
|
||||
|
||||
cdef _to_arrays(self, intp_t [:] keys, float64_t [:] values):
|
||||
# Internal version of to_arrays that takes already-initialized arrays
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = self.my_map.begin()
|
||||
cdef cpp_map[intp_t, float64_t].iterator end = self.my_map.end()
|
||||
cdef int index = 0
|
||||
while it != end:
|
||||
keys[index] = deref(it).first
|
||||
values[index] = deref(it).second
|
||||
inc(it)
|
||||
index += 1
|
||||
|
||||
def update(self, IntFloatDict other):
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = other.my_map.begin()
|
||||
cdef cpp_map[intp_t, float64_t].iterator end = other.my_map.end()
|
||||
while it != end:
|
||||
self.my_map[deref(it).first] = deref(it).second
|
||||
inc(it)
|
||||
|
||||
def copy(self):
|
||||
cdef IntFloatDict out_obj = IntFloatDict.__new__(IntFloatDict)
|
||||
# The '=' operator is a copy operator for C++ maps
|
||||
out_obj.my_map = self.my_map
|
||||
return out_obj
|
||||
|
||||
def append(self, intp_t key, float64_t value):
|
||||
# Construct our arguments
|
||||
cdef pair[intp_t, float64_t] args
|
||||
args.first = key
|
||||
args.second = value
|
||||
self.my_map.insert(args)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# operation on dict
|
||||
|
||||
def argmin(IntFloatDict d):
|
||||
cdef cpp_map[intp_t, float64_t].iterator it = d.my_map.begin()
|
||||
cdef cpp_map[intp_t, float64_t].iterator end = d.my_map.end()
|
||||
cdef intp_t min_key = -1
|
||||
cdef float64_t min_value = np.inf
|
||||
while it != end:
|
||||
if deref(it).second < min_value:
|
||||
min_value = deref(it).second
|
||||
min_key = deref(it).first
|
||||
inc(it)
|
||||
return min_key, min_value
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# Heap routines, used in various Cython implementations.
|
||||
|
||||
from cython cimport floating
|
||||
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
|
||||
cdef int heap_push(
|
||||
floating* values,
|
||||
intp_t* indices,
|
||||
intp_t size,
|
||||
floating val,
|
||||
intp_t val_idx,
|
||||
) noexcept nogil
|
||||
# Heap routines, used in various Cython implementations.
|
||||
|
||||
from cython cimport floating
|
||||
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
|
||||
cdef int heap_push(
|
||||
floating* values,
|
||||
intp_t* indices,
|
||||
intp_t size,
|
||||
floating val,
|
||||
intp_t val_idx,
|
||||
) noexcept nogil
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
from cython cimport floating
|
||||
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
|
||||
cdef inline int heap_push(
|
||||
floating* values,
|
||||
intp_t* indices,
|
||||
intp_t size,
|
||||
floating val,
|
||||
intp_t val_idx,
|
||||
) noexcept nogil:
|
||||
"""Push a tuple (val, val_idx) onto a fixed-size max-heap.
|
||||
|
||||
The max-heap is represented as a Structure of Arrays where:
|
||||
- values is the array containing the data to construct the heap with
|
||||
- indices is the array containing the indices (meta-data) of each value
|
||||
|
||||
Notes
|
||||
-----
|
||||
Arrays are manipulated via a pointer to there first element and their size
|
||||
as to ease the processing of dynamically allocated buffers.
|
||||
|
||||
For instance, in pseudo-code:
|
||||
|
||||
values = [1.2, 0.4, 0.1],
|
||||
indices = [42, 1, 5],
|
||||
heap_push(
|
||||
values=values,
|
||||
indices=indices,
|
||||
size=3,
|
||||
val=0.2,
|
||||
val_idx=4,
|
||||
)
|
||||
|
||||
will modify values and indices inplace, giving at the end of the call:
|
||||
|
||||
values == [0.4, 0.2, 0.1]
|
||||
indices == [1, 4, 5]
|
||||
|
||||
"""
|
||||
cdef:
|
||||
intp_t current_idx, left_child_idx, right_child_idx, swap_idx
|
||||
|
||||
# Check if val should be in heap
|
||||
if val >= values[0]:
|
||||
return 0
|
||||
|
||||
# Insert val at position zero
|
||||
values[0] = val
|
||||
indices[0] = val_idx
|
||||
|
||||
# Descend the heap, swapping values until the max heap criterion is met
|
||||
current_idx = 0
|
||||
while True:
|
||||
left_child_idx = 2 * current_idx + 1
|
||||
right_child_idx = left_child_idx + 1
|
||||
|
||||
if left_child_idx >= size:
|
||||
break
|
||||
elif right_child_idx >= size:
|
||||
if values[left_child_idx] > val:
|
||||
swap_idx = left_child_idx
|
||||
else:
|
||||
break
|
||||
elif values[left_child_idx] >= values[right_child_idx]:
|
||||
if val < values[left_child_idx]:
|
||||
swap_idx = left_child_idx
|
||||
else:
|
||||
break
|
||||
else:
|
||||
if val < values[right_child_idx]:
|
||||
swap_idx = right_child_idx
|
||||
else:
|
||||
break
|
||||
|
||||
values[current_idx] = values[swap_idx]
|
||||
indices[current_idx] = indices[swap_idx]
|
||||
|
||||
current_idx = swap_idx
|
||||
|
||||
values[current_idx] = val
|
||||
indices[current_idx] = val_idx
|
||||
|
||||
return 0
|
||||
from cython cimport floating
|
||||
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
|
||||
cdef inline int heap_push(
|
||||
floating* values,
|
||||
intp_t* indices,
|
||||
intp_t size,
|
||||
floating val,
|
||||
intp_t val_idx,
|
||||
) noexcept nogil:
|
||||
"""Push a tuple (val, val_idx) onto a fixed-size max-heap.
|
||||
|
||||
The max-heap is represented as a Structure of Arrays where:
|
||||
- values is the array containing the data to construct the heap with
|
||||
- indices is the array containing the indices (meta-data) of each value
|
||||
|
||||
Notes
|
||||
-----
|
||||
Arrays are manipulated via a pointer to there first element and their size
|
||||
as to ease the processing of dynamically allocated buffers.
|
||||
|
||||
For instance, in pseudo-code:
|
||||
|
||||
values = [1.2, 0.4, 0.1],
|
||||
indices = [42, 1, 5],
|
||||
heap_push(
|
||||
values=values,
|
||||
indices=indices,
|
||||
size=3,
|
||||
val=0.2,
|
||||
val_idx=4,
|
||||
)
|
||||
|
||||
will modify values and indices inplace, giving at the end of the call:
|
||||
|
||||
values == [0.4, 0.2, 0.1]
|
||||
indices == [1, 4, 5]
|
||||
|
||||
"""
|
||||
cdef:
|
||||
intp_t current_idx, left_child_idx, right_child_idx, swap_idx
|
||||
|
||||
# Check if val should be in heap
|
||||
if val >= values[0]:
|
||||
return 0
|
||||
|
||||
# Insert val at position zero
|
||||
values[0] = val
|
||||
indices[0] = val_idx
|
||||
|
||||
# Descend the heap, swapping values until the max heap criterion is met
|
||||
current_idx = 0
|
||||
while True:
|
||||
left_child_idx = 2 * current_idx + 1
|
||||
right_child_idx = left_child_idx + 1
|
||||
|
||||
if left_child_idx >= size:
|
||||
break
|
||||
elif right_child_idx >= size:
|
||||
if values[left_child_idx] > val:
|
||||
swap_idx = left_child_idx
|
||||
else:
|
||||
break
|
||||
elif values[left_child_idx] >= values[right_child_idx]:
|
||||
if val < values[left_child_idx]:
|
||||
swap_idx = left_child_idx
|
||||
else:
|
||||
break
|
||||
else:
|
||||
if val < values[right_child_idx]:
|
||||
swap_idx = right_child_idx
|
||||
else:
|
||||
break
|
||||
|
||||
values[current_idx] = values[swap_idx]
|
||||
indices[current_idx] = indices[swap_idx]
|
||||
|
||||
current_idx = swap_idx
|
||||
|
||||
values[current_idx] = val
|
||||
indices[current_idx] = val_idx
|
||||
|
||||
return 0
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from libc.math cimport isnan, isinf
|
||||
from cython cimport floating
|
||||
|
||||
|
||||
cpdef enum FiniteStatus:
|
||||
all_finite = 0
|
||||
has_nan = 1
|
||||
has_infinite = 2
|
||||
|
||||
|
||||
def cy_isfinite(floating[::1] a, bint allow_nan=False):
|
||||
cdef FiniteStatus result
|
||||
with nogil:
|
||||
result = _isfinite(a, allow_nan)
|
||||
return result
|
||||
|
||||
|
||||
cdef inline FiniteStatus _isfinite(floating[::1] a, bint allow_nan) noexcept nogil:
|
||||
cdef floating* a_ptr = &a[0]
|
||||
cdef Py_ssize_t length = len(a)
|
||||
if allow_nan:
|
||||
return _isfinite_allow_nan(a_ptr, length)
|
||||
else:
|
||||
return _isfinite_disable_nan(a_ptr, length)
|
||||
|
||||
|
||||
cdef inline FiniteStatus _isfinite_allow_nan(floating* a_ptr,
|
||||
Py_ssize_t length) noexcept nogil:
|
||||
cdef Py_ssize_t i
|
||||
cdef floating v
|
||||
for i in range(length):
|
||||
v = a_ptr[i]
|
||||
if isinf(v):
|
||||
return FiniteStatus.has_infinite
|
||||
return FiniteStatus.all_finite
|
||||
|
||||
|
||||
cdef inline FiniteStatus _isfinite_disable_nan(floating* a_ptr,
|
||||
Py_ssize_t length) noexcept nogil:
|
||||
cdef Py_ssize_t i
|
||||
cdef floating v
|
||||
for i in range(length):
|
||||
v = a_ptr[i]
|
||||
if isnan(v):
|
||||
return FiniteStatus.has_nan
|
||||
elif isinf(v):
|
||||
return FiniteStatus.has_infinite
|
||||
return FiniteStatus.all_finite
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from libc.math cimport isnan, isinf
|
||||
from cython cimport floating
|
||||
|
||||
|
||||
cpdef enum FiniteStatus:
|
||||
all_finite = 0
|
||||
has_nan = 1
|
||||
has_infinite = 2
|
||||
|
||||
|
||||
def cy_isfinite(floating[::1] a, bint allow_nan=False):
|
||||
cdef FiniteStatus result
|
||||
with nogil:
|
||||
result = _isfinite(a, allow_nan)
|
||||
return result
|
||||
|
||||
|
||||
cdef inline FiniteStatus _isfinite(floating[::1] a, bint allow_nan) noexcept nogil:
|
||||
cdef floating* a_ptr = &a[0]
|
||||
cdef Py_ssize_t length = len(a)
|
||||
if allow_nan:
|
||||
return _isfinite_allow_nan(a_ptr, length)
|
||||
else:
|
||||
return _isfinite_disable_nan(a_ptr, length)
|
||||
|
||||
|
||||
cdef inline FiniteStatus _isfinite_allow_nan(floating* a_ptr,
|
||||
Py_ssize_t length) noexcept nogil:
|
||||
cdef Py_ssize_t i
|
||||
cdef floating v
|
||||
for i in range(length):
|
||||
v = a_ptr[i]
|
||||
if isinf(v):
|
||||
return FiniteStatus.has_infinite
|
||||
return FiniteStatus.all_finite
|
||||
|
||||
|
||||
cdef inline FiniteStatus _isfinite_disable_nan(floating* a_ptr,
|
||||
Py_ssize_t length) noexcept nogil:
|
||||
cdef Py_ssize_t i
|
||||
cdef floating v
|
||||
for i in range(length):
|
||||
v = a_ptr[i]
|
||||
if isnan(v):
|
||||
return FiniteStatus.has_nan
|
||||
elif isinf(v):
|
||||
return FiniteStatus.has_infinite
|
||||
return FiniteStatus.all_finite
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
# Helpers to safely access OpenMP routines
|
||||
#
|
||||
# no-op implementations are provided for the case where OpenMP is not available.
|
||||
#
|
||||
# All calls to OpenMP routines should be cimported from this module.
|
||||
|
||||
cdef extern from *:
|
||||
"""
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#define SKLEARN_OPENMP_PARALLELISM_ENABLED 1
|
||||
#else
|
||||
#define SKLEARN_OPENMP_PARALLELISM_ENABLED 0
|
||||
#define omp_lock_t int
|
||||
#define omp_init_lock(l) (void)0
|
||||
#define omp_destroy_lock(l) (void)0
|
||||
#define omp_set_lock(l) (void)0
|
||||
#define omp_unset_lock(l) (void)0
|
||||
#define omp_get_thread_num() 0
|
||||
#define omp_get_max_threads() 1
|
||||
#endif
|
||||
"""
|
||||
bint SKLEARN_OPENMP_PARALLELISM_ENABLED
|
||||
|
||||
ctypedef struct omp_lock_t:
|
||||
pass
|
||||
|
||||
void omp_init_lock(omp_lock_t*) noexcept nogil
|
||||
void omp_destroy_lock(omp_lock_t*) noexcept nogil
|
||||
void omp_set_lock(omp_lock_t*) noexcept nogil
|
||||
void omp_unset_lock(omp_lock_t*) noexcept nogil
|
||||
int omp_get_thread_num() noexcept nogil
|
||||
int omp_get_max_threads() noexcept nogil
|
||||
# Helpers to safely access OpenMP routines
|
||||
#
|
||||
# no-op implementations are provided for the case where OpenMP is not available.
|
||||
#
|
||||
# All calls to OpenMP routines should be cimported from this module.
|
||||
|
||||
cdef extern from *:
|
||||
"""
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#define SKLEARN_OPENMP_PARALLELISM_ENABLED 1
|
||||
#else
|
||||
#define SKLEARN_OPENMP_PARALLELISM_ENABLED 0
|
||||
#define omp_lock_t int
|
||||
#define omp_init_lock(l) (void)0
|
||||
#define omp_destroy_lock(l) (void)0
|
||||
#define omp_set_lock(l) (void)0
|
||||
#define omp_unset_lock(l) (void)0
|
||||
#define omp_get_thread_num() 0
|
||||
#define omp_get_max_threads() 1
|
||||
#endif
|
||||
"""
|
||||
bint SKLEARN_OPENMP_PARALLELISM_ENABLED
|
||||
|
||||
ctypedef struct omp_lock_t:
|
||||
pass
|
||||
|
||||
void omp_init_lock(omp_lock_t*) noexcept nogil
|
||||
void omp_destroy_lock(omp_lock_t*) noexcept nogil
|
||||
void omp_set_lock(omp_lock_t*) noexcept nogil
|
||||
void omp_unset_lock(omp_lock_t*) noexcept nogil
|
||||
int omp_get_thread_num() noexcept nogil
|
||||
int omp_get_max_threads() noexcept nogil
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
import os
|
||||
from joblib import cpu_count
|
||||
|
||||
|
||||
# Module level cache for cpu_count as we do not expect this to change during
|
||||
# the lifecycle of a Python program. This dictionary is keyed by
|
||||
# only_physical_cores.
|
||||
_CPU_COUNTS = {}
|
||||
|
||||
|
||||
def _openmp_parallelism_enabled():
|
||||
"""Determines whether scikit-learn has been built with OpenMP
|
||||
|
||||
It allows to retrieve at runtime the information gathered at compile time.
|
||||
"""
|
||||
# SKLEARN_OPENMP_PARALLELISM_ENABLED is resolved at compile time and defined
|
||||
# in _openmp_helpers.pxd as a boolean. This function exposes it to Python.
|
||||
return SKLEARN_OPENMP_PARALLELISM_ENABLED
|
||||
|
||||
|
||||
cpdef _openmp_effective_n_threads(n_threads=None, only_physical_cores=True):
|
||||
"""Determine the effective number of threads to be used for OpenMP calls
|
||||
|
||||
- For ``n_threads = None``,
|
||||
- if the ``OMP_NUM_THREADS`` environment variable is set, return
|
||||
``openmp.omp_get_max_threads()``
|
||||
- otherwise, return the minimum between ``openmp.omp_get_max_threads()``
|
||||
and the number of cpus, taking cgroups quotas into account. Cgroups
|
||||
quotas can typically be set by tools such as Docker.
|
||||
The result of ``omp_get_max_threads`` can be influenced by environment
|
||||
variable ``OMP_NUM_THREADS`` or at runtime by ``omp_set_num_threads``.
|
||||
|
||||
- For ``n_threads > 0``, return this as the maximal number of threads for
|
||||
parallel OpenMP calls.
|
||||
|
||||
- For ``n_threads < 0``, return the maximal number of threads minus
|
||||
``|n_threads + 1|``. In particular ``n_threads = -1`` will use as many
|
||||
threads as there are available cores on the machine.
|
||||
|
||||
- Raise a ValueError for ``n_threads = 0``.
|
||||
|
||||
Passing the `only_physical_cores=False` flag makes it possible to use extra
|
||||
threads for SMT/HyperThreading logical cores. It has been empirically
|
||||
observed that using as many threads as available SMT cores can slightly
|
||||
improve the performance in some cases, but can severely degrade
|
||||
performance other times. Therefore it is recommended to use
|
||||
`only_physical_cores=True` unless an empirical study has been conducted to
|
||||
assess the impact of SMT on a case-by-case basis (using various input data
|
||||
shapes, in particular small data shapes).
|
||||
|
||||
If scikit-learn is built without OpenMP support, always return 1.
|
||||
"""
|
||||
if n_threads == 0:
|
||||
raise ValueError("n_threads = 0 is invalid")
|
||||
|
||||
if not SKLEARN_OPENMP_PARALLELISM_ENABLED:
|
||||
# OpenMP disabled at build-time => sequential mode
|
||||
return 1
|
||||
|
||||
if os.getenv("OMP_NUM_THREADS"):
|
||||
# Fall back to user provided number of threads making it possible
|
||||
# to exceed the number of cpus.
|
||||
max_n_threads = omp_get_max_threads()
|
||||
else:
|
||||
try:
|
||||
n_cpus = _CPU_COUNTS[only_physical_cores]
|
||||
except KeyError:
|
||||
n_cpus = cpu_count(only_physical_cores=only_physical_cores)
|
||||
_CPU_COUNTS[only_physical_cores] = n_cpus
|
||||
max_n_threads = min(omp_get_max_threads(), n_cpus)
|
||||
|
||||
if n_threads is None:
|
||||
return max_n_threads
|
||||
elif n_threads < 0:
|
||||
return max(1, max_n_threads + n_threads + 1)
|
||||
|
||||
return n_threads
|
||||
import os
|
||||
from joblib import cpu_count
|
||||
|
||||
|
||||
# Module level cache for cpu_count as we do not expect this to change during
|
||||
# the lifecycle of a Python program. This dictionary is keyed by
|
||||
# only_physical_cores.
|
||||
_CPU_COUNTS = {}
|
||||
|
||||
|
||||
def _openmp_parallelism_enabled():
|
||||
"""Determines whether scikit-learn has been built with OpenMP
|
||||
|
||||
It allows to retrieve at runtime the information gathered at compile time.
|
||||
"""
|
||||
# SKLEARN_OPENMP_PARALLELISM_ENABLED is resolved at compile time and defined
|
||||
# in _openmp_helpers.pxd as a boolean. This function exposes it to Python.
|
||||
return SKLEARN_OPENMP_PARALLELISM_ENABLED
|
||||
|
||||
|
||||
cpdef _openmp_effective_n_threads(n_threads=None, only_physical_cores=True):
|
||||
"""Determine the effective number of threads to be used for OpenMP calls
|
||||
|
||||
- For ``n_threads = None``,
|
||||
- if the ``OMP_NUM_THREADS`` environment variable is set, return
|
||||
``openmp.omp_get_max_threads()``
|
||||
- otherwise, return the minimum between ``openmp.omp_get_max_threads()``
|
||||
and the number of cpus, taking cgroups quotas into account. Cgroups
|
||||
quotas can typically be set by tools such as Docker.
|
||||
The result of ``omp_get_max_threads`` can be influenced by environment
|
||||
variable ``OMP_NUM_THREADS`` or at runtime by ``omp_set_num_threads``.
|
||||
|
||||
- For ``n_threads > 0``, return this as the maximal number of threads for
|
||||
parallel OpenMP calls.
|
||||
|
||||
- For ``n_threads < 0``, return the maximal number of threads minus
|
||||
``|n_threads + 1|``. In particular ``n_threads = -1`` will use as many
|
||||
threads as there are available cores on the machine.
|
||||
|
||||
- Raise a ValueError for ``n_threads = 0``.
|
||||
|
||||
Passing the `only_physical_cores=False` flag makes it possible to use extra
|
||||
threads for SMT/HyperThreading logical cores. It has been empirically
|
||||
observed that using as many threads as available SMT cores can slightly
|
||||
improve the performance in some cases, but can severely degrade
|
||||
performance other times. Therefore it is recommended to use
|
||||
`only_physical_cores=True` unless an empirical study has been conducted to
|
||||
assess the impact of SMT on a case-by-case basis (using various input data
|
||||
shapes, in particular small data shapes).
|
||||
|
||||
If scikit-learn is built without OpenMP support, always return 1.
|
||||
"""
|
||||
if n_threads == 0:
|
||||
raise ValueError("n_threads = 0 is invalid")
|
||||
|
||||
if not SKLEARN_OPENMP_PARALLELISM_ENABLED:
|
||||
# OpenMP disabled at build-time => sequential mode
|
||||
return 1
|
||||
|
||||
if os.getenv("OMP_NUM_THREADS"):
|
||||
# Fall back to user provided number of threads making it possible
|
||||
# to exceed the number of cpus.
|
||||
max_n_threads = omp_get_max_threads()
|
||||
else:
|
||||
try:
|
||||
n_cpus = _CPU_COUNTS[only_physical_cores]
|
||||
except KeyError:
|
||||
n_cpus = cpu_count(only_physical_cores=only_physical_cores)
|
||||
_CPU_COUNTS[only_physical_cores] = n_cpus
|
||||
max_n_threads = min(omp_get_max_threads(), n_cpus)
|
||||
|
||||
if n_threads is None:
|
||||
return max_n_threads
|
||||
elif n_threads < 0:
|
||||
return max(1, max_n_threads + n_threads + 1)
|
||||
|
||||
return n_threads
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from ._typedefs cimport uint32_t
|
||||
|
||||
|
||||
cdef inline uint32_t DEFAULT_SEED = 1
|
||||
|
||||
cdef enum:
|
||||
# Max value for our rand_r replacement (near the bottom).
|
||||
# We don't use RAND_MAX because it's different across platforms and
|
||||
# particularly tiny on Windows/MSVC.
|
||||
# It corresponds to the maximum representable value for
|
||||
# 32-bit signed integers (i.e. 2^31 - 1).
|
||||
RAND_R_MAX = 2147483647
|
||||
|
||||
|
||||
# rand_r replacement using a 32bit XorShift generator
|
||||
# See http://www.jstatsoft.org/v08/i14/paper for details
|
||||
cdef inline uint32_t our_rand_r(uint32_t* seed) nogil:
|
||||
"""Generate a pseudo-random np.uint32 from a np.uint32 seed"""
|
||||
# seed shouldn't ever be 0.
|
||||
if (seed[0] == 0):
|
||||
seed[0] = DEFAULT_SEED
|
||||
|
||||
seed[0] ^= <uint32_t>(seed[0] << 13)
|
||||
seed[0] ^= <uint32_t>(seed[0] >> 17)
|
||||
seed[0] ^= <uint32_t>(seed[0] << 5)
|
||||
|
||||
# Use the modulo to make sure that we don't return a values greater than the
|
||||
# maximum representable value for signed 32bit integers (i.e. 2^31 - 1).
|
||||
# Note that the parenthesis are needed to avoid overflow: here
|
||||
# RAND_R_MAX is cast to uint32_t before 1 is added.
|
||||
return seed[0] % ((<uint32_t>RAND_R_MAX) + 1)
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from ._typedefs cimport uint32_t
|
||||
|
||||
|
||||
cdef inline uint32_t DEFAULT_SEED = 1
|
||||
|
||||
cdef enum:
|
||||
# Max value for our rand_r replacement (near the bottom).
|
||||
# We don't use RAND_MAX because it's different across platforms and
|
||||
# particularly tiny on Windows/MSVC.
|
||||
# It corresponds to the maximum representable value for
|
||||
# 32-bit signed integers (i.e. 2^31 - 1).
|
||||
RAND_R_MAX = 2147483647
|
||||
|
||||
|
||||
# rand_r replacement using a 32bit XorShift generator
|
||||
# See http://www.jstatsoft.org/v08/i14/paper for details
|
||||
cdef inline uint32_t our_rand_r(uint32_t* seed) nogil:
|
||||
"""Generate a pseudo-random np.uint32 from a np.uint32 seed"""
|
||||
# seed shouldn't ever be 0.
|
||||
if (seed[0] == 0):
|
||||
seed[0] = DEFAULT_SEED
|
||||
|
||||
seed[0] ^= <uint32_t>(seed[0] << 13)
|
||||
seed[0] ^= <uint32_t>(seed[0] >> 17)
|
||||
seed[0] ^= <uint32_t>(seed[0] << 5)
|
||||
|
||||
# Use the modulo to make sure that we don't return a values greater than the
|
||||
# maximum representable value for signed 32bit integers (i.e. 2^31 - 1).
|
||||
# Note that the parenthesis are needed to avoid overflow: here
|
||||
# RAND_R_MAX is cast to uint32_t before 1 is added.
|
||||
return seed[0] % ((<uint32_t>RAND_R_MAX) + 1)
|
||||
|
||||
@@ -1,355 +1,355 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""
|
||||
Random utility function
|
||||
=======================
|
||||
This module complements missing features of ``numpy.random``.
|
||||
|
||||
The module contains:
|
||||
* Several algorithms to sample integers without replacement.
|
||||
* Fast rand_r alternative based on xor shifts
|
||||
"""
|
||||
import numpy as np
|
||||
from . import check_random_state
|
||||
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
|
||||
cdef uint32_t DEFAULT_SEED = 1
|
||||
|
||||
|
||||
# Compatibility type to always accept the default int type used by NumPy, both
|
||||
# before and after NumPy 2. On Windows, `long` does not always match `inp_t`.
|
||||
# See the comments in the `sample_without_replacement` Python function for more
|
||||
# details.
|
||||
ctypedef fused default_int:
|
||||
intp_t
|
||||
long
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_check_input(default_int n_population,
|
||||
default_int n_samples):
|
||||
""" Check that input are consistent for sample_without_replacement"""
|
||||
if n_population < 0:
|
||||
raise ValueError('n_population should be greater than 0, got %s.'
|
||||
% n_population)
|
||||
|
||||
if n_samples > n_population:
|
||||
raise ValueError('n_population should be greater or equal than '
|
||||
'n_samples, got n_samples > n_population (%s > %s)'
|
||||
% (n_samples, n_population))
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_with_tracking_selection(
|
||||
default_int n_population,
|
||||
default_int n_samples,
|
||||
random_state=None):
|
||||
r"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
Time complexity:
|
||||
- Worst-case: unbounded
|
||||
- Average-case:
|
||||
O(O(np.random.randint) * \sum_{i=1}^n_samples 1 /
|
||||
(1 - i / n_population)))
|
||||
<= O(O(np.random.randint) *
|
||||
n_population * ln((n_population - 2)
|
||||
/(n_population - 1 - n_samples)))
|
||||
<= O(O(np.random.randint) *
|
||||
n_population * 1 / (1 - n_samples / n_population))
|
||||
|
||||
Space complexity of O(n_samples) in a python set.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
cdef default_int i
|
||||
cdef default_int j
|
||||
cdef default_int[::1] out = np.empty((n_samples, ), dtype=int)
|
||||
|
||||
rng = check_random_state(random_state)
|
||||
rng_randint = rng.randint
|
||||
|
||||
# The following line of code are heavily inspired from python core,
|
||||
# more precisely of random.sample.
|
||||
cdef set selected = set()
|
||||
|
||||
for i in range(n_samples):
|
||||
j = rng_randint(n_population)
|
||||
while j in selected:
|
||||
j = rng_randint(n_population)
|
||||
selected.add(j)
|
||||
out[i] = j
|
||||
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_with_pool(default_int n_population,
|
||||
default_int n_samples,
|
||||
random_state=None):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
Time complexity: O(n_population + O(np.random.randint) * n_samples)
|
||||
|
||||
Space complexity of O(n_population + n_samples).
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
cdef default_int i
|
||||
cdef default_int j
|
||||
cdef default_int[::1] out = np.empty((n_samples,), dtype=int)
|
||||
cdef default_int[::1] pool = np.empty((n_population,), dtype=int)
|
||||
|
||||
rng = check_random_state(random_state)
|
||||
rng_randint = rng.randint
|
||||
|
||||
# Initialize the pool
|
||||
for i in range(n_population):
|
||||
pool[i] = i
|
||||
|
||||
# The following line of code are heavily inspired from python core,
|
||||
# more precisely of random.sample.
|
||||
for i in range(n_samples):
|
||||
j = rng_randint(n_population - i) # invariant: non-selected at [0,n-i)
|
||||
out[i] = pool[j]
|
||||
pool[j] = pool[n_population - i - 1] # move non-selected item into vacancy
|
||||
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_with_reservoir_sampling(
|
||||
default_int n_population,
|
||||
default_int n_samples,
|
||||
random_state=None
|
||||
):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
Time complexity of
|
||||
O((n_population - n_samples) * O(np.random.randint) + n_samples)
|
||||
Space complexity of O(n_samples)
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer. The order of the items is not
|
||||
necessarily random. Use a random permutation of the array if the order
|
||||
of the items has to be randomized.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
cdef default_int i
|
||||
cdef default_int j
|
||||
cdef default_int[::1] out = np.empty((n_samples, ), dtype=int)
|
||||
|
||||
rng = check_random_state(random_state)
|
||||
rng_randint = rng.randint
|
||||
|
||||
# This cython implementation is based on the one of Robert Kern:
|
||||
# http://mail.scipy.org/pipermail/numpy-discussion/2010-December/
|
||||
# 054289.html
|
||||
#
|
||||
for i in range(n_samples):
|
||||
out[i] = i
|
||||
|
||||
for i from n_samples <= i < n_population:
|
||||
j = rng_randint(0, i + 1)
|
||||
if j < n_samples:
|
||||
out[j] = i
|
||||
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
cdef _sample_without_replacement(default_int n_population,
|
||||
default_int n_samples,
|
||||
method="auto",
|
||||
random_state=None):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Private function for the implementation, see sample_without_replacement
|
||||
documentation for more details.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
all_methods = ("auto", "tracking_selection", "reservoir_sampling", "pool")
|
||||
|
||||
ratio = <double> n_samples / n_population if n_population != 0.0 else 1.0
|
||||
|
||||
# Check ratio and use permutation unless ratio < 0.01 or ratio > 0.99
|
||||
if method == "auto" and ratio > 0.01 and ratio < 0.99:
|
||||
rng = check_random_state(random_state)
|
||||
return rng.permutation(n_population)[:n_samples]
|
||||
|
||||
if method == "auto" or method == "tracking_selection":
|
||||
# TODO the pool based method can also be used.
|
||||
# however, it requires special benchmark to take into account
|
||||
# the memory requirement of the array vs the set.
|
||||
|
||||
# The value 0.2 has been determined through benchmarking.
|
||||
if ratio < 0.2:
|
||||
return _sample_without_replacement_with_tracking_selection(
|
||||
n_population, n_samples, random_state)
|
||||
else:
|
||||
return _sample_without_replacement_with_reservoir_sampling(
|
||||
n_population, n_samples, random_state)
|
||||
|
||||
elif method == "reservoir_sampling":
|
||||
return _sample_without_replacement_with_reservoir_sampling(
|
||||
n_population, n_samples, random_state)
|
||||
|
||||
elif method == "pool":
|
||||
return _sample_without_replacement_with_pool(n_population, n_samples,
|
||||
random_state)
|
||||
else:
|
||||
raise ValueError('Expected a method name in %s, got %s. '
|
||||
% (all_methods, method))
|
||||
|
||||
|
||||
def sample_without_replacement(
|
||||
object n_population, object n_samples, method="auto", random_state=None):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
method : {"auto", "tracking_selection", "reservoir_sampling", "pool"}, \
|
||||
default='auto'
|
||||
If method == "auto", the ratio of n_samples / n_population is used
|
||||
to determine which algorithm to use:
|
||||
If ratio is between 0 and 0.01, tracking selection is used.
|
||||
If ratio is between 0.01 and 0.99, numpy.random.permutation is used.
|
||||
If ratio is greater than 0.99, reservoir sampling is used.
|
||||
The order of the selected integers is undefined. If a random order is
|
||||
desired, the selected subset should be shuffled.
|
||||
|
||||
If method =="tracking_selection", a set based implementation is used
|
||||
which is suitable for `n_samples` <<< `n_population`.
|
||||
|
||||
If method == "reservoir_sampling", a reservoir sampling algorithm is
|
||||
used which is suitable for high memory constraint or when
|
||||
O(`n_samples`) ~ O(`n_population`).
|
||||
The order of the selected integers is undefined. If a random order is
|
||||
desired, the selected subset should be shuffled.
|
||||
|
||||
If method == "pool", a pool based algorithm is particularly fast, even
|
||||
faster than the tracking selection method. However, a vector containing
|
||||
the entire population has to be initialized.
|
||||
If n_samples ~ n_population, the reservoir sampling method is faster.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer. The subset of selected integer might
|
||||
not be randomized, see the method argument.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from sklearn.utils.random import sample_without_replacement
|
||||
>>> sample_without_replacement(10, 5, random_state=42)
|
||||
array([8, 1, 5, 0, 7])
|
||||
"""
|
||||
cdef:
|
||||
intp_t n_pop_intp, n_samples_intp
|
||||
long n_pop_long, n_samples_long
|
||||
|
||||
# On most platforms `np.int_ is np.intp`. However, before NumPy 2 the
|
||||
# default integer `np.int_` was a long which is 32bit on 64bit windows
|
||||
# while `intp` is 64bit on 64bit platforms and 32bit on 32bit ones.
|
||||
if np.int_ is np.intp:
|
||||
# Branch always taken on NumPy >=2 (or when not on 64bit windows).
|
||||
# Cython has different rules for conversion of values to integers.
|
||||
# For NumPy <1.26.2 AND Cython 3, this first branch requires `int()`
|
||||
# called explicitly to allow e.g. floats.
|
||||
n_pop_intp = int(n_population)
|
||||
n_samples_intp = int(n_samples)
|
||||
return _sample_without_replacement(
|
||||
n_pop_intp, n_samples_intp, method, random_state)
|
||||
else:
|
||||
# Branch taken on 64bit windows with Numpy<2.0 where `long` is 32bit
|
||||
n_pop_long = n_population
|
||||
n_samples_long = n_samples
|
||||
return _sample_without_replacement(
|
||||
n_pop_long, n_samples_long, method, random_state)
|
||||
|
||||
|
||||
def _our_rand_r_py(seed):
|
||||
"""Python utils to test the our_rand_r function"""
|
||||
cdef uint32_t my_seed = seed
|
||||
return our_rand_r(&my_seed)
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""
|
||||
Random utility function
|
||||
=======================
|
||||
This module complements missing features of ``numpy.random``.
|
||||
|
||||
The module contains:
|
||||
* Several algorithms to sample integers without replacement.
|
||||
* Fast rand_r alternative based on xor shifts
|
||||
"""
|
||||
import numpy as np
|
||||
from . import check_random_state
|
||||
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
|
||||
cdef uint32_t DEFAULT_SEED = 1
|
||||
|
||||
|
||||
# Compatibility type to always accept the default int type used by NumPy, both
|
||||
# before and after NumPy 2. On Windows, `long` does not always match `inp_t`.
|
||||
# See the comments in the `sample_without_replacement` Python function for more
|
||||
# details.
|
||||
ctypedef fused default_int:
|
||||
intp_t
|
||||
long
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_check_input(default_int n_population,
|
||||
default_int n_samples):
|
||||
""" Check that input are consistent for sample_without_replacement"""
|
||||
if n_population < 0:
|
||||
raise ValueError('n_population should be greater than 0, got %s.'
|
||||
% n_population)
|
||||
|
||||
if n_samples > n_population:
|
||||
raise ValueError('n_population should be greater or equal than '
|
||||
'n_samples, got n_samples > n_population (%s > %s)'
|
||||
% (n_samples, n_population))
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_with_tracking_selection(
|
||||
default_int n_population,
|
||||
default_int n_samples,
|
||||
random_state=None):
|
||||
r"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
Time complexity:
|
||||
- Worst-case: unbounded
|
||||
- Average-case:
|
||||
O(O(np.random.randint) * \sum_{i=1}^n_samples 1 /
|
||||
(1 - i / n_population)))
|
||||
<= O(O(np.random.randint) *
|
||||
n_population * ln((n_population - 2)
|
||||
/(n_population - 1 - n_samples)))
|
||||
<= O(O(np.random.randint) *
|
||||
n_population * 1 / (1 - n_samples / n_population))
|
||||
|
||||
Space complexity of O(n_samples) in a python set.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
cdef default_int i
|
||||
cdef default_int j
|
||||
cdef default_int[::1] out = np.empty((n_samples, ), dtype=int)
|
||||
|
||||
rng = check_random_state(random_state)
|
||||
rng_randint = rng.randint
|
||||
|
||||
# The following line of code are heavily inspired from python core,
|
||||
# more precisely of random.sample.
|
||||
cdef set selected = set()
|
||||
|
||||
for i in range(n_samples):
|
||||
j = rng_randint(n_population)
|
||||
while j in selected:
|
||||
j = rng_randint(n_population)
|
||||
selected.add(j)
|
||||
out[i] = j
|
||||
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_with_pool(default_int n_population,
|
||||
default_int n_samples,
|
||||
random_state=None):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
Time complexity: O(n_population + O(np.random.randint) * n_samples)
|
||||
|
||||
Space complexity of O(n_population + n_samples).
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
cdef default_int i
|
||||
cdef default_int j
|
||||
cdef default_int[::1] out = np.empty((n_samples,), dtype=int)
|
||||
cdef default_int[::1] pool = np.empty((n_population,), dtype=int)
|
||||
|
||||
rng = check_random_state(random_state)
|
||||
rng_randint = rng.randint
|
||||
|
||||
# Initialize the pool
|
||||
for i in range(n_population):
|
||||
pool[i] = i
|
||||
|
||||
# The following line of code are heavily inspired from python core,
|
||||
# more precisely of random.sample.
|
||||
for i in range(n_samples):
|
||||
j = rng_randint(n_population - i) # invariant: non-selected at [0,n-i)
|
||||
out[i] = pool[j]
|
||||
pool[j] = pool[n_population - i - 1] # move non-selected item into vacancy
|
||||
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
cpdef _sample_without_replacement_with_reservoir_sampling(
|
||||
default_int n_population,
|
||||
default_int n_samples,
|
||||
random_state=None
|
||||
):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
Time complexity of
|
||||
O((n_population - n_samples) * O(np.random.randint) + n_samples)
|
||||
Space complexity of O(n_samples)
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer. The order of the items is not
|
||||
necessarily random. Use a random permutation of the array if the order
|
||||
of the items has to be randomized.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
cdef default_int i
|
||||
cdef default_int j
|
||||
cdef default_int[::1] out = np.empty((n_samples, ), dtype=int)
|
||||
|
||||
rng = check_random_state(random_state)
|
||||
rng_randint = rng.randint
|
||||
|
||||
# This cython implementation is based on the one of Robert Kern:
|
||||
# http://mail.scipy.org/pipermail/numpy-discussion/2010-December/
|
||||
# 054289.html
|
||||
#
|
||||
for i in range(n_samples):
|
||||
out[i] = i
|
||||
|
||||
for i from n_samples <= i < n_population:
|
||||
j = rng_randint(0, i + 1)
|
||||
if j < n_samples:
|
||||
out[j] = i
|
||||
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
cdef _sample_without_replacement(default_int n_population,
|
||||
default_int n_samples,
|
||||
method="auto",
|
||||
random_state=None):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Private function for the implementation, see sample_without_replacement
|
||||
documentation for more details.
|
||||
"""
|
||||
_sample_without_replacement_check_input(n_population, n_samples)
|
||||
|
||||
all_methods = ("auto", "tracking_selection", "reservoir_sampling", "pool")
|
||||
|
||||
ratio = <double> n_samples / n_population if n_population != 0.0 else 1.0
|
||||
|
||||
# Check ratio and use permutation unless ratio < 0.01 or ratio > 0.99
|
||||
if method == "auto" and ratio > 0.01 and ratio < 0.99:
|
||||
rng = check_random_state(random_state)
|
||||
return rng.permutation(n_population)[:n_samples]
|
||||
|
||||
if method == "auto" or method == "tracking_selection":
|
||||
# TODO the pool based method can also be used.
|
||||
# however, it requires special benchmark to take into account
|
||||
# the memory requirement of the array vs the set.
|
||||
|
||||
# The value 0.2 has been determined through benchmarking.
|
||||
if ratio < 0.2:
|
||||
return _sample_without_replacement_with_tracking_selection(
|
||||
n_population, n_samples, random_state)
|
||||
else:
|
||||
return _sample_without_replacement_with_reservoir_sampling(
|
||||
n_population, n_samples, random_state)
|
||||
|
||||
elif method == "reservoir_sampling":
|
||||
return _sample_without_replacement_with_reservoir_sampling(
|
||||
n_population, n_samples, random_state)
|
||||
|
||||
elif method == "pool":
|
||||
return _sample_without_replacement_with_pool(n_population, n_samples,
|
||||
random_state)
|
||||
else:
|
||||
raise ValueError('Expected a method name in %s, got %s. '
|
||||
% (all_methods, method))
|
||||
|
||||
|
||||
def sample_without_replacement(
|
||||
object n_population, object n_samples, method="auto", random_state=None):
|
||||
"""Sample integers without replacement.
|
||||
|
||||
Select n_samples integers from the set [0, n_population) without
|
||||
replacement.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_population : int
|
||||
The size of the set to sample from.
|
||||
|
||||
n_samples : int
|
||||
The number of integer to sample.
|
||||
|
||||
random_state : int, RandomState instance or None, default=None
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`.
|
||||
|
||||
method : {"auto", "tracking_selection", "reservoir_sampling", "pool"}, \
|
||||
default='auto'
|
||||
If method == "auto", the ratio of n_samples / n_population is used
|
||||
to determine which algorithm to use:
|
||||
If ratio is between 0 and 0.01, tracking selection is used.
|
||||
If ratio is between 0.01 and 0.99, numpy.random.permutation is used.
|
||||
If ratio is greater than 0.99, reservoir sampling is used.
|
||||
The order of the selected integers is undefined. If a random order is
|
||||
desired, the selected subset should be shuffled.
|
||||
|
||||
If method =="tracking_selection", a set based implementation is used
|
||||
which is suitable for `n_samples` <<< `n_population`.
|
||||
|
||||
If method == "reservoir_sampling", a reservoir sampling algorithm is
|
||||
used which is suitable for high memory constraint or when
|
||||
O(`n_samples`) ~ O(`n_population`).
|
||||
The order of the selected integers is undefined. If a random order is
|
||||
desired, the selected subset should be shuffled.
|
||||
|
||||
If method == "pool", a pool based algorithm is particularly fast, even
|
||||
faster than the tracking selection method. However, a vector containing
|
||||
the entire population has to be initialized.
|
||||
If n_samples ~ n_population, the reservoir sampling method is faster.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray of shape (n_samples,)
|
||||
The sampled subsets of integer. The subset of selected integer might
|
||||
not be randomized, see the method argument.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from sklearn.utils.random import sample_without_replacement
|
||||
>>> sample_without_replacement(10, 5, random_state=42)
|
||||
array([8, 1, 5, 0, 7])
|
||||
"""
|
||||
cdef:
|
||||
intp_t n_pop_intp, n_samples_intp
|
||||
long n_pop_long, n_samples_long
|
||||
|
||||
# On most platforms `np.int_ is np.intp`. However, before NumPy 2 the
|
||||
# default integer `np.int_` was a long which is 32bit on 64bit windows
|
||||
# while `intp` is 64bit on 64bit platforms and 32bit on 32bit ones.
|
||||
if np.int_ is np.intp:
|
||||
# Branch always taken on NumPy >=2 (or when not on 64bit windows).
|
||||
# Cython has different rules for conversion of values to integers.
|
||||
# For NumPy <1.26.2 AND Cython 3, this first branch requires `int()`
|
||||
# called explicitly to allow e.g. floats.
|
||||
n_pop_intp = int(n_population)
|
||||
n_samples_intp = int(n_samples)
|
||||
return _sample_without_replacement(
|
||||
n_pop_intp, n_samples_intp, method, random_state)
|
||||
else:
|
||||
# Branch taken on 64bit windows with Numpy<2.0 where `long` is 32bit
|
||||
n_pop_long = n_population
|
||||
n_samples_long = n_samples
|
||||
return _sample_without_replacement(
|
||||
n_pop_long, n_samples_long, method, random_state)
|
||||
|
||||
|
||||
def _our_rand_r_py(seed):
|
||||
"""Python utils to test the our_rand_r function"""
|
||||
cdef uint32_t my_seed = seed
|
||||
return our_rand_r(&my_seed)
|
||||
|
||||
+413
-413
@@ -1,413 +1,413 @@
|
||||
#$id {
|
||||
/* Definition of color scheme common for light and dark mode */
|
||||
--sklearn-color-text: #000;
|
||||
--sklearn-color-text-muted: #666;
|
||||
--sklearn-color-line: gray;
|
||||
/* Definition of color scheme for unfitted estimators */
|
||||
--sklearn-color-unfitted-level-0: #fff5e6;
|
||||
--sklearn-color-unfitted-level-1: #f6e4d2;
|
||||
--sklearn-color-unfitted-level-2: #ffe0b3;
|
||||
--sklearn-color-unfitted-level-3: chocolate;
|
||||
/* Definition of color scheme for fitted estimators */
|
||||
--sklearn-color-fitted-level-0: #f0f8ff;
|
||||
--sklearn-color-fitted-level-1: #d4ebff;
|
||||
--sklearn-color-fitted-level-2: #b3dbfd;
|
||||
--sklearn-color-fitted-level-3: cornflowerblue;
|
||||
|
||||
/* Specific color for light theme */
|
||||
--sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));
|
||||
--sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));
|
||||
--sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));
|
||||
--sklearn-color-icon: #696969;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* Redefinition of color scheme for dark theme */
|
||||
--sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));
|
||||
--sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));
|
||||
--sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));
|
||||
--sklearn-color-icon: #878787;
|
||||
}
|
||||
}
|
||||
|
||||
#$id {
|
||||
color: var(--sklearn-color-text);
|
||||
}
|
||||
|
||||
#$id pre {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#$id input.sk-hidden--visually {
|
||||
border: 0;
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
#$id div.sk-dashed-wrapped {
|
||||
border: 1px dashed var(--sklearn-color-line);
|
||||
margin: 0 0.4em 0.5em 0.4em;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 0.4em;
|
||||
background-color: var(--sklearn-color-background);
|
||||
}
|
||||
|
||||
#$id div.sk-container {
|
||||
/* jupyter's `normalize.less` sets `[hidden] { display: none; }`
|
||||
but bootstrap.min.css set `[hidden] { display: none !important; }`
|
||||
so we also need the `!important` here to be able to override the
|
||||
default hidden behavior on the sphinx rendered scikit-learn.org.
|
||||
See: https://github.com/scikit-learn/scikit-learn/issues/21755 */
|
||||
display: inline-block !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#$id div.sk-text-repr-fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sk-parallel-item,
|
||||
div.sk-serial,
|
||||
div.sk-item {
|
||||
/* draw centered vertical line to link estimators */
|
||||
background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));
|
||||
background-size: 2px 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
/* Parallel-specific style estimator block */
|
||||
|
||||
#$id div.sk-parallel-item::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
border-bottom: 2px solid var(--sklearn-color-text-on-default-background);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
background-color: var(--sklearn-color-background);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item:first-child::after {
|
||||
align-self: flex-end;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item:last-child::after {
|
||||
align-self: flex-start;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item:only-child::after {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
/* Serial-specific style estimator block */
|
||||
|
||||
#$id div.sk-serial {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: var(--sklearn-color-background);
|
||||
padding-right: 1em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is
|
||||
clickable and can be expanded/collapsed.
|
||||
- Pipeline and ColumnTransformer use this feature and define the default style
|
||||
- Estimators will overwrite some part of the style using the `sk-estimator` class
|
||||
*/
|
||||
|
||||
/* Pipeline and ColumnTransformer style (default) */
|
||||
|
||||
#$id div.sk-toggleable {
|
||||
/* Default theme specific background. It is overwritten whether we have a
|
||||
specific estimator or a Pipeline/ColumnTransformer */
|
||||
background-color: var(--sklearn-color-background);
|
||||
}
|
||||
|
||||
/* Toggleable label */
|
||||
#$id label.sk-toggleable__label {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
#$id label.sk-toggleable__label .caption {
|
||||
font-size: 0.6rem;
|
||||
font-weight: lighter;
|
||||
color: var(--sklearn-color-text-muted);
|
||||
}
|
||||
|
||||
#$id label.sk-toggleable__label-arrow:before {
|
||||
/* Arrow on the left of the label */
|
||||
content: "▸";
|
||||
float: left;
|
||||
margin-right: 0.25em;
|
||||
color: var(--sklearn-color-icon);
|
||||
}
|
||||
|
||||
#$id label.sk-toggleable__label-arrow:hover:before {
|
||||
color: var(--sklearn-color-text);
|
||||
}
|
||||
|
||||
/* Toggleable content - dropdown */
|
||||
|
||||
#$id div.sk-toggleable__content {
|
||||
display: none;
|
||||
text-align: left;
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-toggleable__content.fitted {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-toggleable__content pre {
|
||||
margin: 0.2em;
|
||||
border-radius: 0.25em;
|
||||
color: var(--sklearn-color-text);
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-toggleable__content.fitted pre {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-fitted-level-0);
|
||||
}
|
||||
|
||||
#$id input.sk-toggleable__control:checked~div.sk-toggleable__content {
|
||||
/* Expand drop-down */
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#$id input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {
|
||||
content: "▾";
|
||||
}
|
||||
|
||||
/* Pipeline/ColumnTransformer-specific style */
|
||||
|
||||
#$id div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
color: var(--sklearn-color-text);
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
/* Estimator-specific style */
|
||||
|
||||
/* Colorize estimator box */
|
||||
#$id div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-label label.sk-toggleable__label,
|
||||
#$id div.sk-label label {
|
||||
/* The background is the default theme color */
|
||||
color: var(--sklearn-color-text-on-default-background);
|
||||
}
|
||||
|
||||
/* On hover, darken the color of the background */
|
||||
#$id div.sk-label:hover label.sk-toggleable__label {
|
||||
color: var(--sklearn-color-text);
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
/* Label box, darken color on hover, fitted */
|
||||
#$id div.sk-label.fitted:hover label.sk-toggleable__label.fitted {
|
||||
color: var(--sklearn-color-text);
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
/* Estimator label */
|
||||
|
||||
#$id div.sk-label label {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
#$id div.sk-label-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Estimator-specific */
|
||||
#$id div.sk-estimator {
|
||||
font-family: monospace;
|
||||
border: 1px dotted var(--sklearn-color-border-box);
|
||||
border-radius: 0.25em;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 0.5em;
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-estimator.fitted {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-0);
|
||||
}
|
||||
|
||||
/* on hover */
|
||||
#$id div.sk-estimator:hover {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-estimator.fitted:hover {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
/* Specification for estimator info (e.g. "i" and "?") */
|
||||
|
||||
/* Common style for "i" and "?" */
|
||||
|
||||
.sk-estimator-doc-link,
|
||||
a:link.sk-estimator-doc-link,
|
||||
a:visited.sk-estimator-doc-link {
|
||||
float: right;
|
||||
font-size: smaller;
|
||||
line-height: 1em;
|
||||
font-family: monospace;
|
||||
background-color: var(--sklearn-color-background);
|
||||
border-radius: 1em;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
text-decoration: none !important;
|
||||
margin-left: 0.5em;
|
||||
text-align: center;
|
||||
/* unfitted */
|
||||
border: var(--sklearn-color-unfitted-level-1) 1pt solid;
|
||||
color: var(--sklearn-color-unfitted-level-1);
|
||||
}
|
||||
|
||||
.sk-estimator-doc-link.fitted,
|
||||
a:link.sk-estimator-doc-link.fitted,
|
||||
a:visited.sk-estimator-doc-link.fitted {
|
||||
/* fitted */
|
||||
border: var(--sklearn-color-fitted-level-1) 1pt solid;
|
||||
color: var(--sklearn-color-fitted-level-1);
|
||||
}
|
||||
|
||||
/* On hover */
|
||||
div.sk-estimator:hover .sk-estimator-doc-link:hover,
|
||||
.sk-estimator-doc-link:hover,
|
||||
div.sk-label-container:hover .sk-estimator-doc-link:hover,
|
||||
.sk-estimator-doc-link:hover {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-3);
|
||||
color: var(--sklearn-color-background);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,
|
||||
.sk-estimator-doc-link.fitted:hover,
|
||||
div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,
|
||||
.sk-estimator-doc-link.fitted:hover {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-3);
|
||||
color: var(--sklearn-color-background);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Span, style for the box shown on hovering the info icon */
|
||||
.sk-estimator-doc-link span {
|
||||
display: none;
|
||||
z-index: 9999;
|
||||
position: relative;
|
||||
font-weight: normal;
|
||||
right: .2ex;
|
||||
padding: .5ex;
|
||||
margin: .5ex;
|
||||
width: min-content;
|
||||
min-width: 20ex;
|
||||
max-width: 50ex;
|
||||
color: var(--sklearn-color-text);
|
||||
box-shadow: 2pt 2pt 4pt #999;
|
||||
/* unfitted */
|
||||
background: var(--sklearn-color-unfitted-level-0);
|
||||
border: .5pt solid var(--sklearn-color-unfitted-level-3);
|
||||
}
|
||||
|
||||
.sk-estimator-doc-link.fitted span {
|
||||
/* fitted */
|
||||
background: var(--sklearn-color-fitted-level-0);
|
||||
border: var(--sklearn-color-fitted-level-3);
|
||||
}
|
||||
|
||||
.sk-estimator-doc-link:hover span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* "?"-specific style due to the `<a>` HTML tag */
|
||||
|
||||
#$id a.estimator_doc_link {
|
||||
float: right;
|
||||
font-size: 1rem;
|
||||
line-height: 1em;
|
||||
font-family: monospace;
|
||||
background-color: var(--sklearn-color-background);
|
||||
border-radius: 1rem;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
text-decoration: none;
|
||||
/* unfitted */
|
||||
color: var(--sklearn-color-unfitted-level-1);
|
||||
border: var(--sklearn-color-unfitted-level-1) 1pt solid;
|
||||
}
|
||||
|
||||
#$id a.estimator_doc_link.fitted {
|
||||
/* fitted */
|
||||
border: var(--sklearn-color-fitted-level-1) 1pt solid;
|
||||
color: var(--sklearn-color-fitted-level-1);
|
||||
}
|
||||
|
||||
/* On hover */
|
||||
#$id a.estimator_doc_link:hover {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-3);
|
||||
color: var(--sklearn-color-background);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#$id a.estimator_doc_link.fitted:hover {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-3);
|
||||
}
|
||||
#$id {
|
||||
/* Definition of color scheme common for light and dark mode */
|
||||
--sklearn-color-text: #000;
|
||||
--sklearn-color-text-muted: #666;
|
||||
--sklearn-color-line: gray;
|
||||
/* Definition of color scheme for unfitted estimators */
|
||||
--sklearn-color-unfitted-level-0: #fff5e6;
|
||||
--sklearn-color-unfitted-level-1: #f6e4d2;
|
||||
--sklearn-color-unfitted-level-2: #ffe0b3;
|
||||
--sklearn-color-unfitted-level-3: chocolate;
|
||||
/* Definition of color scheme for fitted estimators */
|
||||
--sklearn-color-fitted-level-0: #f0f8ff;
|
||||
--sklearn-color-fitted-level-1: #d4ebff;
|
||||
--sklearn-color-fitted-level-2: #b3dbfd;
|
||||
--sklearn-color-fitted-level-3: cornflowerblue;
|
||||
|
||||
/* Specific color for light theme */
|
||||
--sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));
|
||||
--sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));
|
||||
--sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));
|
||||
--sklearn-color-icon: #696969;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* Redefinition of color scheme for dark theme */
|
||||
--sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));
|
||||
--sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));
|
||||
--sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));
|
||||
--sklearn-color-icon: #878787;
|
||||
}
|
||||
}
|
||||
|
||||
#$id {
|
||||
color: var(--sklearn-color-text);
|
||||
}
|
||||
|
||||
#$id pre {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#$id input.sk-hidden--visually {
|
||||
border: 0;
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
#$id div.sk-dashed-wrapped {
|
||||
border: 1px dashed var(--sklearn-color-line);
|
||||
margin: 0 0.4em 0.5em 0.4em;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 0.4em;
|
||||
background-color: var(--sklearn-color-background);
|
||||
}
|
||||
|
||||
#$id div.sk-container {
|
||||
/* jupyter's `normalize.less` sets `[hidden] { display: none; }`
|
||||
but bootstrap.min.css set `[hidden] { display: none !important; }`
|
||||
so we also need the `!important` here to be able to override the
|
||||
default hidden behavior on the sphinx rendered scikit-learn.org.
|
||||
See: https://github.com/scikit-learn/scikit-learn/issues/21755 */
|
||||
display: inline-block !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#$id div.sk-text-repr-fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sk-parallel-item,
|
||||
div.sk-serial,
|
||||
div.sk-item {
|
||||
/* draw centered vertical line to link estimators */
|
||||
background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));
|
||||
background-size: 2px 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
/* Parallel-specific style estimator block */
|
||||
|
||||
#$id div.sk-parallel-item::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
border-bottom: 2px solid var(--sklearn-color-text-on-default-background);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
background-color: var(--sklearn-color-background);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item:first-child::after {
|
||||
align-self: flex-end;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item:last-child::after {
|
||||
align-self: flex-start;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#$id div.sk-parallel-item:only-child::after {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
/* Serial-specific style estimator block */
|
||||
|
||||
#$id div.sk-serial {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: var(--sklearn-color-background);
|
||||
padding-right: 1em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is
|
||||
clickable and can be expanded/collapsed.
|
||||
- Pipeline and ColumnTransformer use this feature and define the default style
|
||||
- Estimators will overwrite some part of the style using the `sk-estimator` class
|
||||
*/
|
||||
|
||||
/* Pipeline and ColumnTransformer style (default) */
|
||||
|
||||
#$id div.sk-toggleable {
|
||||
/* Default theme specific background. It is overwritten whether we have a
|
||||
specific estimator or a Pipeline/ColumnTransformer */
|
||||
background-color: var(--sklearn-color-background);
|
||||
}
|
||||
|
||||
/* Toggleable label */
|
||||
#$id label.sk-toggleable__label {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
#$id label.sk-toggleable__label .caption {
|
||||
font-size: 0.6rem;
|
||||
font-weight: lighter;
|
||||
color: var(--sklearn-color-text-muted);
|
||||
}
|
||||
|
||||
#$id label.sk-toggleable__label-arrow:before {
|
||||
/* Arrow on the left of the label */
|
||||
content: "▸";
|
||||
float: left;
|
||||
margin-right: 0.25em;
|
||||
color: var(--sklearn-color-icon);
|
||||
}
|
||||
|
||||
#$id label.sk-toggleable__label-arrow:hover:before {
|
||||
color: var(--sklearn-color-text);
|
||||
}
|
||||
|
||||
/* Toggleable content - dropdown */
|
||||
|
||||
#$id div.sk-toggleable__content {
|
||||
display: none;
|
||||
text-align: left;
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-toggleable__content.fitted {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-toggleable__content pre {
|
||||
margin: 0.2em;
|
||||
border-radius: 0.25em;
|
||||
color: var(--sklearn-color-text);
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-toggleable__content.fitted pre {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-fitted-level-0);
|
||||
}
|
||||
|
||||
#$id input.sk-toggleable__control:checked~div.sk-toggleable__content {
|
||||
/* Expand drop-down */
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#$id input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {
|
||||
content: "▾";
|
||||
}
|
||||
|
||||
/* Pipeline/ColumnTransformer-specific style */
|
||||
|
||||
#$id div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
color: var(--sklearn-color-text);
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
/* Estimator-specific style */
|
||||
|
||||
/* Colorize estimator box */
|
||||
#$id div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-label label.sk-toggleable__label,
|
||||
#$id div.sk-label label {
|
||||
/* The background is the default theme color */
|
||||
color: var(--sklearn-color-text-on-default-background);
|
||||
}
|
||||
|
||||
/* On hover, darken the color of the background */
|
||||
#$id div.sk-label:hover label.sk-toggleable__label {
|
||||
color: var(--sklearn-color-text);
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
/* Label box, darken color on hover, fitted */
|
||||
#$id div.sk-label.fitted:hover label.sk-toggleable__label.fitted {
|
||||
color: var(--sklearn-color-text);
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
/* Estimator label */
|
||||
|
||||
#$id div.sk-label label {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
#$id div.sk-label-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Estimator-specific */
|
||||
#$id div.sk-estimator {
|
||||
font-family: monospace;
|
||||
border: 1px dotted var(--sklearn-color-border-box);
|
||||
border-radius: 0.25em;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 0.5em;
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-0);
|
||||
}
|
||||
|
||||
#$id div.sk-estimator.fitted {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-0);
|
||||
}
|
||||
|
||||
/* on hover */
|
||||
#$id div.sk-estimator:hover {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-2);
|
||||
}
|
||||
|
||||
#$id div.sk-estimator.fitted:hover {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-2);
|
||||
}
|
||||
|
||||
/* Specification for estimator info (e.g. "i" and "?") */
|
||||
|
||||
/* Common style for "i" and "?" */
|
||||
|
||||
.sk-estimator-doc-link,
|
||||
a:link.sk-estimator-doc-link,
|
||||
a:visited.sk-estimator-doc-link {
|
||||
float: right;
|
||||
font-size: smaller;
|
||||
line-height: 1em;
|
||||
font-family: monospace;
|
||||
background-color: var(--sklearn-color-background);
|
||||
border-radius: 1em;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
text-decoration: none !important;
|
||||
margin-left: 0.5em;
|
||||
text-align: center;
|
||||
/* unfitted */
|
||||
border: var(--sklearn-color-unfitted-level-1) 1pt solid;
|
||||
color: var(--sklearn-color-unfitted-level-1);
|
||||
}
|
||||
|
||||
.sk-estimator-doc-link.fitted,
|
||||
a:link.sk-estimator-doc-link.fitted,
|
||||
a:visited.sk-estimator-doc-link.fitted {
|
||||
/* fitted */
|
||||
border: var(--sklearn-color-fitted-level-1) 1pt solid;
|
||||
color: var(--sklearn-color-fitted-level-1);
|
||||
}
|
||||
|
||||
/* On hover */
|
||||
div.sk-estimator:hover .sk-estimator-doc-link:hover,
|
||||
.sk-estimator-doc-link:hover,
|
||||
div.sk-label-container:hover .sk-estimator-doc-link:hover,
|
||||
.sk-estimator-doc-link:hover {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-3);
|
||||
color: var(--sklearn-color-background);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,
|
||||
.sk-estimator-doc-link.fitted:hover,
|
||||
div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,
|
||||
.sk-estimator-doc-link.fitted:hover {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-3);
|
||||
color: var(--sklearn-color-background);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Span, style for the box shown on hovering the info icon */
|
||||
.sk-estimator-doc-link span {
|
||||
display: none;
|
||||
z-index: 9999;
|
||||
position: relative;
|
||||
font-weight: normal;
|
||||
right: .2ex;
|
||||
padding: .5ex;
|
||||
margin: .5ex;
|
||||
width: min-content;
|
||||
min-width: 20ex;
|
||||
max-width: 50ex;
|
||||
color: var(--sklearn-color-text);
|
||||
box-shadow: 2pt 2pt 4pt #999;
|
||||
/* unfitted */
|
||||
background: var(--sklearn-color-unfitted-level-0);
|
||||
border: .5pt solid var(--sklearn-color-unfitted-level-3);
|
||||
}
|
||||
|
||||
.sk-estimator-doc-link.fitted span {
|
||||
/* fitted */
|
||||
background: var(--sklearn-color-fitted-level-0);
|
||||
border: var(--sklearn-color-fitted-level-3);
|
||||
}
|
||||
|
||||
.sk-estimator-doc-link:hover span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* "?"-specific style due to the `<a>` HTML tag */
|
||||
|
||||
#$id a.estimator_doc_link {
|
||||
float: right;
|
||||
font-size: 1rem;
|
||||
line-height: 1em;
|
||||
font-family: monospace;
|
||||
background-color: var(--sklearn-color-background);
|
||||
border-radius: 1rem;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
text-decoration: none;
|
||||
/* unfitted */
|
||||
color: var(--sklearn-color-unfitted-level-1);
|
||||
border: var(--sklearn-color-unfitted-level-1) 1pt solid;
|
||||
}
|
||||
|
||||
#$id a.estimator_doc_link.fitted {
|
||||
/* fitted */
|
||||
border: var(--sklearn-color-fitted-level-1) 1pt solid;
|
||||
color: var(--sklearn-color-fitted-level-1);
|
||||
}
|
||||
|
||||
/* On hover */
|
||||
#$id a.estimator_doc_link:hover {
|
||||
/* unfitted */
|
||||
background-color: var(--sklearn-color-unfitted-level-3);
|
||||
color: var(--sklearn-color-background);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#$id a.estimator_doc_link.fitted:hover {
|
||||
/* fitted */
|
||||
background-color: var(--sklearn-color-fitted-level-3);
|
||||
}
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
function copyToClipboard(text, element) {
|
||||
// Get the parameter prefix from the closest toggleable content
|
||||
const toggleableContent = element.closest('.sk-toggleable__content');
|
||||
const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';
|
||||
const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;
|
||||
|
||||
const originalStyle = element.style;
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
const originalWidth = computedStyle.width;
|
||||
const originalHTML = element.innerHTML.replace('Copied!', '');
|
||||
|
||||
navigator.clipboard.writeText(fullParamName)
|
||||
.then(() => {
|
||||
element.style.width = originalWidth;
|
||||
element.style.color = 'green';
|
||||
element.innerHTML = "Copied!";
|
||||
|
||||
setTimeout(() => {
|
||||
element.innerHTML = originalHTML;
|
||||
element.style = originalStyle;
|
||||
}, 2000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
element.style.color = 'red';
|
||||
element.innerHTML = "Failed!";
|
||||
setTimeout(() => {
|
||||
element.innerHTML = originalHTML;
|
||||
element.style = originalStyle;
|
||||
}, 2000);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {
|
||||
const toggleableContent = element.closest('.sk-toggleable__content');
|
||||
const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';
|
||||
const paramName = element.parentElement.nextElementSibling.textContent.trim();
|
||||
const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;
|
||||
|
||||
element.setAttribute('title', fullParamName);
|
||||
});
|
||||
function copyToClipboard(text, element) {
|
||||
// Get the parameter prefix from the closest toggleable content
|
||||
const toggleableContent = element.closest('.sk-toggleable__content');
|
||||
const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';
|
||||
const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;
|
||||
|
||||
const originalStyle = element.style;
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
const originalWidth = computedStyle.width;
|
||||
const originalHTML = element.innerHTML.replace('Copied!', '');
|
||||
|
||||
navigator.clipboard.writeText(fullParamName)
|
||||
.then(() => {
|
||||
element.style.width = originalWidth;
|
||||
element.style.color = 'green';
|
||||
element.innerHTML = "Copied!";
|
||||
|
||||
setTimeout(() => {
|
||||
element.innerHTML = originalHTML;
|
||||
element.style = originalStyle;
|
||||
}, 2000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
element.style.color = 'red';
|
||||
element.innerHTML = "Failed!";
|
||||
setTimeout(() => {
|
||||
element.innerHTML = originalHTML;
|
||||
element.style = originalStyle;
|
||||
}, 2000);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {
|
||||
const toggleableContent = element.closest('.sk-toggleable__content');
|
||||
const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';
|
||||
const paramName = element.parentElement.nextElementSibling.textContent.trim();
|
||||
const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;
|
||||
|
||||
element.setAttribute('title', fullParamName);
|
||||
});
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
.estimator-table summary {
|
||||
padding: .5rem;
|
||||
font-family: monospace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.estimator-table details[open] {
|
||||
padding-left: 0.1rem;
|
||||
padding-right: 0.1rem;
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table tr:nth-child(odd) {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table tr:nth-child(even) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table tr:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.estimator-table table td {
|
||||
border: 1px solid rgba(106, 105, 104, 0.232);
|
||||
}
|
||||
|
||||
.user-set td {
|
||||
color:rgb(255, 94, 0);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-set td.value pre {
|
||||
color:rgb(255, 94, 0) !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.default td {
|
||||
color: black;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-set td i,
|
||||
.default td i {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.copy-paste-icon {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNy4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjUgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTIwOCAwTDMzMi4xIDBjMTIuNyAwIDI0LjkgNS4xIDMzLjkgMTQuMWw2Ny45IDY3LjljOSA5IDE0LjEgMjEuMiAxNC4xIDMzLjlMNDQ4IDMzNmMwIDI2LjUtMjEuNSA0OC00OCA0OGwtMTkyIDBjLTI2LjUgMC00OC0yMS41LTQ4LTQ4bDAtMjg4YzAtMjYuNSAyMS41LTQ4IDQ4LTQ4ek00OCAxMjhsODAgMCAwIDY0LTY0IDAgMCAyNTYgMTkyIDAgMC0zMiA2NCAwIDAgNDhjMCAyNi41LTIxLjUgNDgtNDggNDhMNDggNTEyYy0yNi41IDAtNDgtMjEuNS00OC00OEwwIDE3NmMwLTI2LjUgMjEuNS00OCA0OC00OHoiLz48L3N2Zz4=);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 14px 14px;
|
||||
background-position: 0;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.estimator-table summary {
|
||||
padding: .5rem;
|
||||
font-family: monospace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.estimator-table details[open] {
|
||||
padding-left: 0.1rem;
|
||||
padding-right: 0.1rem;
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table tr:nth-child(odd) {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table tr:nth-child(even) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.estimator-table .parameters-table tr:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.estimator-table table td {
|
||||
border: 1px solid rgba(106, 105, 104, 0.232);
|
||||
}
|
||||
|
||||
.user-set td {
|
||||
color:rgb(255, 94, 0);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-set td.value pre {
|
||||
color:rgb(255, 94, 0) !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.default td {
|
||||
color: black;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-set td i,
|
||||
.default td i {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.copy-paste-icon {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNy4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjUgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTIwOCAwTDMzMi4xIDBjMTIuNyAwIDI0LjkgNS4xIDMzLjkgMTQuMWw2Ny45IDY3LjljOSA5IDE0LjEgMjEuMiAxNC4xIDMzLjlMNDQ4IDMzNmMwIDI2LjUtMjEuNSA0OC00OCA0OGwtMTkyIDBjLTI2LjUgMC00OC0yMS41LTQ4LTQ4bDAtMjg4YzAtMjYuNSAyMS41LTQ4IDQ4LTQ4ek00OCAxMjhsODAgMCAwIDY0LTY0IDAgMCAyNTYgMTkyIDAgMC0zMiA2NCAwIDAgNDhjMCAyNi41LTIxLjUgNDgtNDggNDhMNDggNTEyYy0yNi41IDAtNDgtMjEuNS00OC00OEwwIDE3NmMwLTI2LjUgMjEuNS00OCA0OC00OHoiLz48L3N2Zz4=);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 14px 14px;
|
||||
background-position: 0;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Dataset abstractions for sequential data access.
|
||||
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: _seq_dataset.pxd
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# name_suffix, c_type
|
||||
dtypes = [('64', 'float64_t'),
|
||||
('32', 'float32_t')]
|
||||
|
||||
}}
|
||||
"""Dataset abstractions for sequential data access."""
|
||||
|
||||
from ._typedefs cimport float32_t, float64_t, intp_t, uint32_t
|
||||
|
||||
# SequentialDataset and its two concrete subclasses are (optionally randomized)
|
||||
# iterators over the rows of a matrix X and corresponding target values y.
|
||||
|
||||
{{for name_suffix, c_type in dtypes}}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
cdef class SequentialDataset{{name_suffix}}:
|
||||
cdef int current_index
|
||||
cdef int[::1] index
|
||||
cdef int *index_data_ptr
|
||||
cdef Py_ssize_t n_samples
|
||||
cdef uint32_t seed
|
||||
|
||||
cdef void shuffle(self, uint32_t seed) noexcept nogil
|
||||
cdef int _get_next_index(self) noexcept nogil
|
||||
cdef int _get_random_index(self) noexcept nogil
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil
|
||||
cdef void next(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil
|
||||
cdef int random(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil
|
||||
|
||||
|
||||
cdef class ArrayDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
cdef const {{c_type}}[:, ::1] X
|
||||
cdef const {{c_type}}[::1] Y
|
||||
cdef const {{c_type}}[::1] sample_weights
|
||||
cdef Py_ssize_t n_features
|
||||
cdef intp_t X_stride
|
||||
cdef {{c_type}} *X_data_ptr
|
||||
cdef {{c_type}} *Y_data_ptr
|
||||
cdef const int[::1] feature_indices
|
||||
cdef int *feature_indices_ptr
|
||||
cdef {{c_type}} *sample_weight_data
|
||||
|
||||
|
||||
cdef class CSRDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
cdef const {{c_type}}[::1] X_data
|
||||
cdef const int[::1] X_indptr
|
||||
cdef const int[::1] X_indices
|
||||
cdef const {{c_type}}[::1] Y
|
||||
cdef const {{c_type}}[::1] sample_weights
|
||||
cdef {{c_type}} *X_data_ptr
|
||||
cdef int *X_indptr_ptr
|
||||
cdef int *X_indices_ptr
|
||||
cdef {{c_type}} *Y_data_ptr
|
||||
cdef {{c_type}} *sample_weight_data
|
||||
|
||||
{{endfor}}
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Dataset abstractions for sequential data access.
|
||||
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: _seq_dataset.pxd
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# name_suffix, c_type
|
||||
dtypes = [('64', 'float64_t'),
|
||||
('32', 'float32_t')]
|
||||
|
||||
}}
|
||||
"""Dataset abstractions for sequential data access."""
|
||||
|
||||
from ._typedefs cimport float32_t, float64_t, intp_t, uint32_t
|
||||
|
||||
# SequentialDataset and its two concrete subclasses are (optionally randomized)
|
||||
# iterators over the rows of a matrix X and corresponding target values y.
|
||||
|
||||
{{for name_suffix, c_type in dtypes}}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
cdef class SequentialDataset{{name_suffix}}:
|
||||
cdef int current_index
|
||||
cdef int[::1] index
|
||||
cdef int *index_data_ptr
|
||||
cdef Py_ssize_t n_samples
|
||||
cdef uint32_t seed
|
||||
|
||||
cdef void shuffle(self, uint32_t seed) noexcept nogil
|
||||
cdef int _get_next_index(self) noexcept nogil
|
||||
cdef int _get_random_index(self) noexcept nogil
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil
|
||||
cdef void next(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil
|
||||
cdef int random(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil
|
||||
|
||||
|
||||
cdef class ArrayDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
cdef const {{c_type}}[:, ::1] X
|
||||
cdef const {{c_type}}[::1] Y
|
||||
cdef const {{c_type}}[::1] sample_weights
|
||||
cdef Py_ssize_t n_features
|
||||
cdef intp_t X_stride
|
||||
cdef {{c_type}} *X_data_ptr
|
||||
cdef {{c_type}} *Y_data_ptr
|
||||
cdef const int[::1] feature_indices
|
||||
cdef int *feature_indices_ptr
|
||||
cdef {{c_type}} *sample_weight_data
|
||||
|
||||
|
||||
cdef class CSRDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
cdef const {{c_type}}[::1] X_data
|
||||
cdef const int[::1] X_indptr
|
||||
cdef const int[::1] X_indices
|
||||
cdef const {{c_type}}[::1] Y
|
||||
cdef const {{c_type}}[::1] sample_weights
|
||||
cdef {{c_type}} *X_data_ptr
|
||||
cdef int *X_indptr_ptr
|
||||
cdef int *X_indices_ptr
|
||||
cdef {{c_type}} *Y_data_ptr
|
||||
cdef {{c_type}} *sample_weight_data
|
||||
|
||||
{{endfor}}
|
||||
|
||||
@@ -1,348 +1,348 @@
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Dataset abstractions for sequential data access.
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: _seq_dataset.pyx
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# name_suffix, c_type, np_type
|
||||
dtypes = [('64', 'float64_t', 'np.float64'),
|
||||
('32', 'float32_t', 'np.float32')]
|
||||
|
||||
}}
|
||||
"""Dataset abstractions for sequential data access."""
|
||||
|
||||
import numpy as np
|
||||
|
||||
cimport cython
|
||||
from libc.limits cimport INT_MAX
|
||||
|
||||
from ._random cimport our_rand_r
|
||||
from ._typedefs cimport float32_t, float64_t, uint32_t
|
||||
|
||||
{{for name_suffix, c_type, np_type in dtypes}}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
cdef class SequentialDataset{{name_suffix}}:
|
||||
"""Base class for datasets with sequential data access.
|
||||
|
||||
SequentialDataset is used to iterate over the rows of a matrix X and
|
||||
corresponding target values y, i.e. to iterate over samples.
|
||||
There are two methods to get the next sample:
|
||||
- next : Iterate sequentially (optionally randomized)
|
||||
- random : Iterate randomly (with replacement)
|
||||
|
||||
Attributes
|
||||
----------
|
||||
index : np.ndarray
|
||||
Index array for fast shuffling.
|
||||
|
||||
index_data_ptr : int
|
||||
Pointer to the index array.
|
||||
|
||||
current_index : int
|
||||
Index of current sample in ``index``.
|
||||
The index of current sample in the data is given by
|
||||
index_data_ptr[current_index].
|
||||
|
||||
n_samples : Py_ssize_t
|
||||
Number of samples in the dataset.
|
||||
|
||||
seed : uint32_t
|
||||
Seed used for random sampling. This attribute is modified at each call to the
|
||||
`random` method.
|
||||
"""
|
||||
|
||||
cdef void next(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil:
|
||||
"""Get the next example ``x`` from the dataset.
|
||||
|
||||
This method gets the next sample looping sequentially over all samples.
|
||||
The order can be shuffled with the method ``shuffle``.
|
||||
Shuffling once before iterating over all samples corresponds to a
|
||||
random draw without replacement. It is used for instance in SGD solver.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}**
|
||||
A pointer to the {{c_type}} array which holds the feature
|
||||
values of the next example.
|
||||
|
||||
x_ind_ptr : np.intc**
|
||||
A pointer to the int array which holds the feature
|
||||
indices of the next example.
|
||||
|
||||
nnz : int*
|
||||
A pointer to an int holding the number of non-zero
|
||||
values of the next example.
|
||||
|
||||
y : {{c_type}}*
|
||||
The target value of the next example.
|
||||
|
||||
sample_weight : {{c_type}}*
|
||||
The weight of the next example.
|
||||
"""
|
||||
cdef int current_index = self._get_next_index()
|
||||
self._sample(x_data_ptr, x_ind_ptr, nnz, y, sample_weight,
|
||||
current_index)
|
||||
|
||||
cdef int random(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil:
|
||||
"""Get a random example ``x`` from the dataset.
|
||||
|
||||
This method gets next sample chosen randomly over a uniform
|
||||
distribution. It corresponds to a random draw with replacement.
|
||||
It is used for instance in SAG solver.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}**
|
||||
A pointer to the {{c_type}} array which holds the feature
|
||||
values of the next example.
|
||||
|
||||
x_ind_ptr : np.intc**
|
||||
A pointer to the int array which holds the feature
|
||||
indices of the next example.
|
||||
|
||||
nnz : int*
|
||||
A pointer to an int holding the number of non-zero
|
||||
values of the next example.
|
||||
|
||||
y : {{c_type}}*
|
||||
The target value of the next example.
|
||||
|
||||
sample_weight : {{c_type}}*
|
||||
The weight of the next example.
|
||||
|
||||
Returns
|
||||
-------
|
||||
current_index : int
|
||||
Index of current sample.
|
||||
"""
|
||||
cdef int current_index = self._get_random_index()
|
||||
self._sample(x_data_ptr, x_ind_ptr, nnz, y, sample_weight,
|
||||
current_index)
|
||||
return current_index
|
||||
|
||||
cdef void shuffle(self, uint32_t seed) noexcept nogil:
|
||||
"""Permutes the ordering of examples."""
|
||||
# Fisher-Yates shuffle
|
||||
cdef int *ind = self.index_data_ptr
|
||||
cdef int n = self.n_samples
|
||||
cdef unsigned i, j
|
||||
for i in range(n - 1):
|
||||
j = i + our_rand_r(&seed) % (n - i)
|
||||
ind[i], ind[j] = ind[j], ind[i]
|
||||
|
||||
cdef int _get_next_index(self) noexcept nogil:
|
||||
cdef int current_index = self.current_index
|
||||
if current_index >= (self.n_samples - 1):
|
||||
current_index = -1
|
||||
|
||||
current_index += 1
|
||||
self.current_index = current_index
|
||||
return self.current_index
|
||||
|
||||
cdef int _get_random_index(self) noexcept nogil:
|
||||
cdef int n = self.n_samples
|
||||
cdef int current_index = our_rand_r(&self.seed) % n
|
||||
self.current_index = current_index
|
||||
return current_index
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil:
|
||||
pass
|
||||
|
||||
def _shuffle_py(self, uint32_t seed):
|
||||
"""python function used for easy testing"""
|
||||
self.shuffle(seed)
|
||||
|
||||
def _next_py(self):
|
||||
"""python function used for easy testing"""
|
||||
cdef int current_index = self._get_next_index()
|
||||
return self._sample_py(current_index)
|
||||
|
||||
def _random_py(self):
|
||||
"""python function used for easy testing"""
|
||||
cdef int current_index = self._get_random_index()
|
||||
return self._sample_py(current_index)
|
||||
|
||||
def _sample_py(self, int current_index):
|
||||
"""python function used for easy testing"""
|
||||
cdef {{c_type}}* x_data_ptr
|
||||
cdef int* x_indices_ptr
|
||||
cdef int nnz, j
|
||||
cdef {{c_type}} y, sample_weight
|
||||
|
||||
# call _sample in cython
|
||||
self._sample(&x_data_ptr, &x_indices_ptr, &nnz, &y, &sample_weight,
|
||||
current_index)
|
||||
|
||||
# transform the pointed data in numpy CSR array
|
||||
cdef {{c_type}}[:] x_data = np.empty(nnz, dtype={{np_type}})
|
||||
cdef int[:] x_indices = np.empty(nnz, dtype=np.int32)
|
||||
cdef int[:] x_indptr = np.asarray([0, nnz], dtype=np.int32)
|
||||
|
||||
for j in range(nnz):
|
||||
x_data[j] = x_data_ptr[j]
|
||||
x_indices[j] = x_indices_ptr[j]
|
||||
|
||||
cdef int sample_idx = self.index_data_ptr[current_index]
|
||||
|
||||
return (
|
||||
(np.asarray(x_data), np.asarray(x_indices), np.asarray(x_indptr)),
|
||||
y,
|
||||
sample_weight,
|
||||
sample_idx,
|
||||
)
|
||||
|
||||
|
||||
cdef class ArrayDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
"""Dataset backed by a two-dimensional numpy array.
|
||||
|
||||
The dtype of the numpy array is expected to be ``{{np_type}}`` ({{c_type}})
|
||||
and C-style memory layout.
|
||||
"""
|
||||
|
||||
def __cinit__(
|
||||
self,
|
||||
const {{c_type}}[:, ::1] X,
|
||||
const {{c_type}}[::1] Y,
|
||||
const {{c_type}}[::1] sample_weights,
|
||||
uint32_t seed=1,
|
||||
):
|
||||
"""A ``SequentialDataset`` backed by a two-dimensional numpy array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : ndarray, dtype={{c_type}}, ndim=2, mode='c'
|
||||
The sample array, of shape(n_samples, n_features)
|
||||
|
||||
Y : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The target array, of shape(n_samples, )
|
||||
|
||||
sample_weights : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The weight of each sample, of shape(n_samples,)
|
||||
"""
|
||||
if X.shape[0] > INT_MAX or X.shape[1] > INT_MAX:
|
||||
raise ValueError("More than %d samples or features not supported;"
|
||||
" got (%d, %d)."
|
||||
% (INT_MAX, X.shape[0], X.shape[1]))
|
||||
|
||||
# keep a reference to the data to prevent garbage collection
|
||||
self.X = X
|
||||
self.Y = Y
|
||||
self.sample_weights = sample_weights
|
||||
|
||||
self.n_samples = X.shape[0]
|
||||
self.n_features = X.shape[1]
|
||||
|
||||
self.feature_indices = np.arange(0, self.n_features, dtype=np.intc)
|
||||
self.feature_indices_ptr = <int *> &self.feature_indices[0]
|
||||
|
||||
self.current_index = -1
|
||||
self.X_stride = X.strides[0] // X.itemsize
|
||||
self.X_data_ptr = <{{c_type}} *> &X[0, 0]
|
||||
self.Y_data_ptr = <{{c_type}} *> &Y[0]
|
||||
self.sample_weight_data = <{{c_type}} *> &sample_weights[0]
|
||||
|
||||
# Use index array for fast shuffling
|
||||
self.index = np.arange(0, self.n_samples, dtype=np.intc)
|
||||
self.index_data_ptr = <int *> &self.index[0]
|
||||
# seed should not be 0 for our_rand_r
|
||||
self.seed = max(seed, 1)
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil:
|
||||
cdef long long sample_idx = self.index_data_ptr[current_index]
|
||||
cdef long long offset = sample_idx * self.X_stride
|
||||
|
||||
y[0] = self.Y_data_ptr[sample_idx]
|
||||
x_data_ptr[0] = self.X_data_ptr + offset
|
||||
x_ind_ptr[0] = self.feature_indices_ptr
|
||||
nnz[0] = self.n_features
|
||||
sample_weight[0] = self.sample_weight_data[sample_idx]
|
||||
|
||||
|
||||
cdef class CSRDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
"""A ``SequentialDataset`` backed by a scipy sparse CSR matrix. """
|
||||
|
||||
def __cinit__(
|
||||
self,
|
||||
const {{c_type}}[::1] X_data,
|
||||
const int[::1] X_indptr,
|
||||
const int[::1] X_indices,
|
||||
const {{c_type}}[::1] Y,
|
||||
const {{c_type}}[::1] sample_weights,
|
||||
uint32_t seed=1,
|
||||
):
|
||||
"""Dataset backed by a scipy sparse CSR matrix.
|
||||
|
||||
The feature indices of ``x`` are given by x_ind_ptr[0:nnz].
|
||||
The corresponding feature values are given by
|
||||
x_data_ptr[0:nnz].
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X_data : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The data array of the CSR features matrix.
|
||||
|
||||
X_indptr : ndarray, dtype=np.intc, ndim=1, mode='c'
|
||||
The index pointer array of the CSR features matrix.
|
||||
|
||||
X_indices : ndarray, dtype=np.intc, ndim=1, mode='c'
|
||||
The column indices array of the CSR features matrix.
|
||||
|
||||
Y : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The target values.
|
||||
|
||||
sample_weights : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The weight of each sample.
|
||||
"""
|
||||
# keep a reference to the data to prevent garbage collection
|
||||
self.X_data = X_data
|
||||
self.X_indptr = X_indptr
|
||||
self.X_indices = X_indices
|
||||
self.Y = Y
|
||||
self.sample_weights = sample_weights
|
||||
|
||||
self.n_samples = Y.shape[0]
|
||||
self.current_index = -1
|
||||
self.X_data_ptr = <{{c_type}} *> &X_data[0]
|
||||
self.X_indptr_ptr = <int *> &X_indptr[0]
|
||||
self.X_indices_ptr = <int *> &X_indices[0]
|
||||
|
||||
self.Y_data_ptr = <{{c_type}} *> &Y[0]
|
||||
self.sample_weight_data = <{{c_type}} *> &sample_weights[0]
|
||||
|
||||
# Use index array for fast shuffling
|
||||
self.index = np.arange(self.n_samples, dtype=np.intc)
|
||||
self.index_data_ptr = <int *> &self.index[0]
|
||||
# seed should not be 0 for our_rand_r
|
||||
self.seed = max(seed, 1)
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil:
|
||||
cdef long long sample_idx = self.index_data_ptr[current_index]
|
||||
cdef long long offset = self.X_indptr_ptr[sample_idx]
|
||||
y[0] = self.Y_data_ptr[sample_idx]
|
||||
x_data_ptr[0] = self.X_data_ptr + offset
|
||||
x_ind_ptr[0] = self.X_indices_ptr + offset
|
||||
nnz[0] = self.X_indptr_ptr[sample_idx + 1] - offset
|
||||
sample_weight[0] = self.sample_weight_data[sample_idx]
|
||||
|
||||
|
||||
{{endfor}}
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Dataset abstractions for sequential data access.
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: _seq_dataset.pyx
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# name_suffix, c_type, np_type
|
||||
dtypes = [('64', 'float64_t', 'np.float64'),
|
||||
('32', 'float32_t', 'np.float32')]
|
||||
|
||||
}}
|
||||
"""Dataset abstractions for sequential data access."""
|
||||
|
||||
import numpy as np
|
||||
|
||||
cimport cython
|
||||
from libc.limits cimport INT_MAX
|
||||
|
||||
from ._random cimport our_rand_r
|
||||
from ._typedefs cimport float32_t, float64_t, uint32_t
|
||||
|
||||
{{for name_suffix, c_type, np_type in dtypes}}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
cdef class SequentialDataset{{name_suffix}}:
|
||||
"""Base class for datasets with sequential data access.
|
||||
|
||||
SequentialDataset is used to iterate over the rows of a matrix X and
|
||||
corresponding target values y, i.e. to iterate over samples.
|
||||
There are two methods to get the next sample:
|
||||
- next : Iterate sequentially (optionally randomized)
|
||||
- random : Iterate randomly (with replacement)
|
||||
|
||||
Attributes
|
||||
----------
|
||||
index : np.ndarray
|
||||
Index array for fast shuffling.
|
||||
|
||||
index_data_ptr : int
|
||||
Pointer to the index array.
|
||||
|
||||
current_index : int
|
||||
Index of current sample in ``index``.
|
||||
The index of current sample in the data is given by
|
||||
index_data_ptr[current_index].
|
||||
|
||||
n_samples : Py_ssize_t
|
||||
Number of samples in the dataset.
|
||||
|
||||
seed : uint32_t
|
||||
Seed used for random sampling. This attribute is modified at each call to the
|
||||
`random` method.
|
||||
"""
|
||||
|
||||
cdef void next(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil:
|
||||
"""Get the next example ``x`` from the dataset.
|
||||
|
||||
This method gets the next sample looping sequentially over all samples.
|
||||
The order can be shuffled with the method ``shuffle``.
|
||||
Shuffling once before iterating over all samples corresponds to a
|
||||
random draw without replacement. It is used for instance in SGD solver.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}**
|
||||
A pointer to the {{c_type}} array which holds the feature
|
||||
values of the next example.
|
||||
|
||||
x_ind_ptr : np.intc**
|
||||
A pointer to the int array which holds the feature
|
||||
indices of the next example.
|
||||
|
||||
nnz : int*
|
||||
A pointer to an int holding the number of non-zero
|
||||
values of the next example.
|
||||
|
||||
y : {{c_type}}*
|
||||
The target value of the next example.
|
||||
|
||||
sample_weight : {{c_type}}*
|
||||
The weight of the next example.
|
||||
"""
|
||||
cdef int current_index = self._get_next_index()
|
||||
self._sample(x_data_ptr, x_ind_ptr, nnz, y, sample_weight,
|
||||
current_index)
|
||||
|
||||
cdef int random(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight) noexcept nogil:
|
||||
"""Get a random example ``x`` from the dataset.
|
||||
|
||||
This method gets next sample chosen randomly over a uniform
|
||||
distribution. It corresponds to a random draw with replacement.
|
||||
It is used for instance in SAG solver.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}**
|
||||
A pointer to the {{c_type}} array which holds the feature
|
||||
values of the next example.
|
||||
|
||||
x_ind_ptr : np.intc**
|
||||
A pointer to the int array which holds the feature
|
||||
indices of the next example.
|
||||
|
||||
nnz : int*
|
||||
A pointer to an int holding the number of non-zero
|
||||
values of the next example.
|
||||
|
||||
y : {{c_type}}*
|
||||
The target value of the next example.
|
||||
|
||||
sample_weight : {{c_type}}*
|
||||
The weight of the next example.
|
||||
|
||||
Returns
|
||||
-------
|
||||
current_index : int
|
||||
Index of current sample.
|
||||
"""
|
||||
cdef int current_index = self._get_random_index()
|
||||
self._sample(x_data_ptr, x_ind_ptr, nnz, y, sample_weight,
|
||||
current_index)
|
||||
return current_index
|
||||
|
||||
cdef void shuffle(self, uint32_t seed) noexcept nogil:
|
||||
"""Permutes the ordering of examples."""
|
||||
# Fisher-Yates shuffle
|
||||
cdef int *ind = self.index_data_ptr
|
||||
cdef int n = self.n_samples
|
||||
cdef unsigned i, j
|
||||
for i in range(n - 1):
|
||||
j = i + our_rand_r(&seed) % (n - i)
|
||||
ind[i], ind[j] = ind[j], ind[i]
|
||||
|
||||
cdef int _get_next_index(self) noexcept nogil:
|
||||
cdef int current_index = self.current_index
|
||||
if current_index >= (self.n_samples - 1):
|
||||
current_index = -1
|
||||
|
||||
current_index += 1
|
||||
self.current_index = current_index
|
||||
return self.current_index
|
||||
|
||||
cdef int _get_random_index(self) noexcept nogil:
|
||||
cdef int n = self.n_samples
|
||||
cdef int current_index = our_rand_r(&self.seed) % n
|
||||
self.current_index = current_index
|
||||
return current_index
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil:
|
||||
pass
|
||||
|
||||
def _shuffle_py(self, uint32_t seed):
|
||||
"""python function used for easy testing"""
|
||||
self.shuffle(seed)
|
||||
|
||||
def _next_py(self):
|
||||
"""python function used for easy testing"""
|
||||
cdef int current_index = self._get_next_index()
|
||||
return self._sample_py(current_index)
|
||||
|
||||
def _random_py(self):
|
||||
"""python function used for easy testing"""
|
||||
cdef int current_index = self._get_random_index()
|
||||
return self._sample_py(current_index)
|
||||
|
||||
def _sample_py(self, int current_index):
|
||||
"""python function used for easy testing"""
|
||||
cdef {{c_type}}* x_data_ptr
|
||||
cdef int* x_indices_ptr
|
||||
cdef int nnz, j
|
||||
cdef {{c_type}} y, sample_weight
|
||||
|
||||
# call _sample in cython
|
||||
self._sample(&x_data_ptr, &x_indices_ptr, &nnz, &y, &sample_weight,
|
||||
current_index)
|
||||
|
||||
# transform the pointed data in numpy CSR array
|
||||
cdef {{c_type}}[:] x_data = np.empty(nnz, dtype={{np_type}})
|
||||
cdef int[:] x_indices = np.empty(nnz, dtype=np.int32)
|
||||
cdef int[:] x_indptr = np.asarray([0, nnz], dtype=np.int32)
|
||||
|
||||
for j in range(nnz):
|
||||
x_data[j] = x_data_ptr[j]
|
||||
x_indices[j] = x_indices_ptr[j]
|
||||
|
||||
cdef int sample_idx = self.index_data_ptr[current_index]
|
||||
|
||||
return (
|
||||
(np.asarray(x_data), np.asarray(x_indices), np.asarray(x_indptr)),
|
||||
y,
|
||||
sample_weight,
|
||||
sample_idx,
|
||||
)
|
||||
|
||||
|
||||
cdef class ArrayDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
"""Dataset backed by a two-dimensional numpy array.
|
||||
|
||||
The dtype of the numpy array is expected to be ``{{np_type}}`` ({{c_type}})
|
||||
and C-style memory layout.
|
||||
"""
|
||||
|
||||
def __cinit__(
|
||||
self,
|
||||
const {{c_type}}[:, ::1] X,
|
||||
const {{c_type}}[::1] Y,
|
||||
const {{c_type}}[::1] sample_weights,
|
||||
uint32_t seed=1,
|
||||
):
|
||||
"""A ``SequentialDataset`` backed by a two-dimensional numpy array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : ndarray, dtype={{c_type}}, ndim=2, mode='c'
|
||||
The sample array, of shape(n_samples, n_features)
|
||||
|
||||
Y : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The target array, of shape(n_samples, )
|
||||
|
||||
sample_weights : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The weight of each sample, of shape(n_samples,)
|
||||
"""
|
||||
if X.shape[0] > INT_MAX or X.shape[1] > INT_MAX:
|
||||
raise ValueError("More than %d samples or features not supported;"
|
||||
" got (%d, %d)."
|
||||
% (INT_MAX, X.shape[0], X.shape[1]))
|
||||
|
||||
# keep a reference to the data to prevent garbage collection
|
||||
self.X = X
|
||||
self.Y = Y
|
||||
self.sample_weights = sample_weights
|
||||
|
||||
self.n_samples = X.shape[0]
|
||||
self.n_features = X.shape[1]
|
||||
|
||||
self.feature_indices = np.arange(0, self.n_features, dtype=np.intc)
|
||||
self.feature_indices_ptr = <int *> &self.feature_indices[0]
|
||||
|
||||
self.current_index = -1
|
||||
self.X_stride = X.strides[0] // X.itemsize
|
||||
self.X_data_ptr = <{{c_type}} *> &X[0, 0]
|
||||
self.Y_data_ptr = <{{c_type}} *> &Y[0]
|
||||
self.sample_weight_data = <{{c_type}} *> &sample_weights[0]
|
||||
|
||||
# Use index array for fast shuffling
|
||||
self.index = np.arange(0, self.n_samples, dtype=np.intc)
|
||||
self.index_data_ptr = <int *> &self.index[0]
|
||||
# seed should not be 0 for our_rand_r
|
||||
self.seed = max(seed, 1)
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil:
|
||||
cdef long long sample_idx = self.index_data_ptr[current_index]
|
||||
cdef long long offset = sample_idx * self.X_stride
|
||||
|
||||
y[0] = self.Y_data_ptr[sample_idx]
|
||||
x_data_ptr[0] = self.X_data_ptr + offset
|
||||
x_ind_ptr[0] = self.feature_indices_ptr
|
||||
nnz[0] = self.n_features
|
||||
sample_weight[0] = self.sample_weight_data[sample_idx]
|
||||
|
||||
|
||||
cdef class CSRDataset{{name_suffix}}(SequentialDataset{{name_suffix}}):
|
||||
"""A ``SequentialDataset`` backed by a scipy sparse CSR matrix. """
|
||||
|
||||
def __cinit__(
|
||||
self,
|
||||
const {{c_type}}[::1] X_data,
|
||||
const int[::1] X_indptr,
|
||||
const int[::1] X_indices,
|
||||
const {{c_type}}[::1] Y,
|
||||
const {{c_type}}[::1] sample_weights,
|
||||
uint32_t seed=1,
|
||||
):
|
||||
"""Dataset backed by a scipy sparse CSR matrix.
|
||||
|
||||
The feature indices of ``x`` are given by x_ind_ptr[0:nnz].
|
||||
The corresponding feature values are given by
|
||||
x_data_ptr[0:nnz].
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X_data : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The data array of the CSR features matrix.
|
||||
|
||||
X_indptr : ndarray, dtype=np.intc, ndim=1, mode='c'
|
||||
The index pointer array of the CSR features matrix.
|
||||
|
||||
X_indices : ndarray, dtype=np.intc, ndim=1, mode='c'
|
||||
The column indices array of the CSR features matrix.
|
||||
|
||||
Y : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The target values.
|
||||
|
||||
sample_weights : ndarray, dtype={{c_type}}, ndim=1, mode='c'
|
||||
The weight of each sample.
|
||||
"""
|
||||
# keep a reference to the data to prevent garbage collection
|
||||
self.X_data = X_data
|
||||
self.X_indptr = X_indptr
|
||||
self.X_indices = X_indices
|
||||
self.Y = Y
|
||||
self.sample_weights = sample_weights
|
||||
|
||||
self.n_samples = Y.shape[0]
|
||||
self.current_index = -1
|
||||
self.X_data_ptr = <{{c_type}} *> &X_data[0]
|
||||
self.X_indptr_ptr = <int *> &X_indptr[0]
|
||||
self.X_indices_ptr = <int *> &X_indices[0]
|
||||
|
||||
self.Y_data_ptr = <{{c_type}} *> &Y[0]
|
||||
self.sample_weight_data = <{{c_type}} *> &sample_weights[0]
|
||||
|
||||
# Use index array for fast shuffling
|
||||
self.index = np.arange(self.n_samples, dtype=np.intc)
|
||||
self.index_data_ptr = <int *> &self.index[0]
|
||||
# seed should not be 0 for our_rand_r
|
||||
self.seed = max(seed, 1)
|
||||
|
||||
cdef void _sample(self, {{c_type}} **x_data_ptr, int **x_ind_ptr,
|
||||
int *nnz, {{c_type}} *y, {{c_type}} *sample_weight,
|
||||
int current_index) noexcept nogil:
|
||||
cdef long long sample_idx = self.index_data_ptr[current_index]
|
||||
cdef long long offset = self.X_indptr_ptr[sample_idx]
|
||||
y[0] = self.Y_data_ptr[sample_idx]
|
||||
x_data_ptr[0] = self.X_data_ptr + offset
|
||||
x_ind_ptr[0] = self.X_indices_ptr + offset
|
||||
nnz[0] = self.X_indptr_ptr[sample_idx + 1] - offset
|
||||
sample_weight[0] = self.sample_weight_data[sample_idx]
|
||||
|
||||
|
||||
{{endfor}}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
from cython cimport floating
|
||||
|
||||
cdef int simultaneous_sort(
|
||||
floating *dist,
|
||||
intp_t *idx,
|
||||
intp_t size,
|
||||
) noexcept nogil
|
||||
from ._typedefs cimport intp_t
|
||||
|
||||
from cython cimport floating
|
||||
|
||||
cdef int simultaneous_sort(
|
||||
floating *dist,
|
||||
intp_t *idx,
|
||||
intp_t size,
|
||||
) noexcept nogil
|
||||
|
||||
@@ -1,93 +1,93 @@
|
||||
from cython cimport floating
|
||||
|
||||
cdef inline void dual_swap(
|
||||
floating* darr,
|
||||
intp_t *iarr,
|
||||
intp_t a,
|
||||
intp_t b,
|
||||
) noexcept nogil:
|
||||
"""Swap the values at index a and b of both darr and iarr"""
|
||||
cdef floating dtmp = darr[a]
|
||||
darr[a] = darr[b]
|
||||
darr[b] = dtmp
|
||||
|
||||
cdef intp_t itmp = iarr[a]
|
||||
iarr[a] = iarr[b]
|
||||
iarr[b] = itmp
|
||||
|
||||
|
||||
cdef int simultaneous_sort(
|
||||
floating* values,
|
||||
intp_t* indices,
|
||||
intp_t size,
|
||||
) noexcept nogil:
|
||||
"""
|
||||
Perform a recursive quicksort on the values array as to sort them ascendingly.
|
||||
This simultaneously performs the swaps on both the values and the indices arrays.
|
||||
|
||||
The numpy equivalent is:
|
||||
|
||||
def simultaneous_sort(dist, idx):
|
||||
i = np.argsort(dist)
|
||||
return dist[i], idx[i]
|
||||
|
||||
Notes
|
||||
-----
|
||||
Arrays are manipulated via a pointer to there first element and their size
|
||||
as to ease the processing of dynamically allocated buffers.
|
||||
"""
|
||||
# TODO: In order to support discrete distance metrics, we need to have a
|
||||
# simultaneous sort which breaks ties on indices when distances are identical.
|
||||
# The best might be using a std::stable_sort and a Comparator which might need
|
||||
# an Array of Structures (AoS) instead of the Structure of Arrays (SoA)
|
||||
# currently used.
|
||||
cdef:
|
||||
intp_t pivot_idx, i, store_idx
|
||||
floating pivot_val
|
||||
|
||||
# in the small-array case, do things efficiently
|
||||
if size <= 1:
|
||||
pass
|
||||
elif size == 2:
|
||||
if values[0] > values[1]:
|
||||
dual_swap(values, indices, 0, 1)
|
||||
elif size == 3:
|
||||
if values[0] > values[1]:
|
||||
dual_swap(values, indices, 0, 1)
|
||||
if values[1] > values[2]:
|
||||
dual_swap(values, indices, 1, 2)
|
||||
if values[0] > values[1]:
|
||||
dual_swap(values, indices, 0, 1)
|
||||
else:
|
||||
# Determine the pivot using the median-of-three rule.
|
||||
# The smallest of the three is moved to the beginning of the array,
|
||||
# the middle (the pivot value) is moved to the end, and the largest
|
||||
# is moved to the pivot index.
|
||||
pivot_idx = size // 2
|
||||
if values[0] > values[size - 1]:
|
||||
dual_swap(values, indices, 0, size - 1)
|
||||
if values[size - 1] > values[pivot_idx]:
|
||||
dual_swap(values, indices, size - 1, pivot_idx)
|
||||
if values[0] > values[size - 1]:
|
||||
dual_swap(values, indices, 0, size - 1)
|
||||
pivot_val = values[size - 1]
|
||||
|
||||
# Partition indices about pivot. At the end of this operation,
|
||||
# pivot_idx will contain the pivot value, everything to the left
|
||||
# will be smaller, and everything to the right will be larger.
|
||||
store_idx = 0
|
||||
for i in range(size - 1):
|
||||
if values[i] < pivot_val:
|
||||
dual_swap(values, indices, i, store_idx)
|
||||
store_idx += 1
|
||||
dual_swap(values, indices, store_idx, size - 1)
|
||||
pivot_idx = store_idx
|
||||
|
||||
# Recursively sort each side of the pivot
|
||||
if pivot_idx > 1:
|
||||
simultaneous_sort(values, indices, pivot_idx)
|
||||
if pivot_idx + 2 < size:
|
||||
simultaneous_sort(values + pivot_idx + 1,
|
||||
indices + pivot_idx + 1,
|
||||
size - pivot_idx - 1)
|
||||
return 0
|
||||
from cython cimport floating
|
||||
|
||||
cdef inline void dual_swap(
|
||||
floating* darr,
|
||||
intp_t *iarr,
|
||||
intp_t a,
|
||||
intp_t b,
|
||||
) noexcept nogil:
|
||||
"""Swap the values at index a and b of both darr and iarr"""
|
||||
cdef floating dtmp = darr[a]
|
||||
darr[a] = darr[b]
|
||||
darr[b] = dtmp
|
||||
|
||||
cdef intp_t itmp = iarr[a]
|
||||
iarr[a] = iarr[b]
|
||||
iarr[b] = itmp
|
||||
|
||||
|
||||
cdef int simultaneous_sort(
|
||||
floating* values,
|
||||
intp_t* indices,
|
||||
intp_t size,
|
||||
) noexcept nogil:
|
||||
"""
|
||||
Perform a recursive quicksort on the values array as to sort them ascendingly.
|
||||
This simultaneously performs the swaps on both the values and the indices arrays.
|
||||
|
||||
The numpy equivalent is:
|
||||
|
||||
def simultaneous_sort(dist, idx):
|
||||
i = np.argsort(dist)
|
||||
return dist[i], idx[i]
|
||||
|
||||
Notes
|
||||
-----
|
||||
Arrays are manipulated via a pointer to there first element and their size
|
||||
as to ease the processing of dynamically allocated buffers.
|
||||
"""
|
||||
# TODO: In order to support discrete distance metrics, we need to have a
|
||||
# simultaneous sort which breaks ties on indices when distances are identical.
|
||||
# The best might be using a std::stable_sort and a Comparator which might need
|
||||
# an Array of Structures (AoS) instead of the Structure of Arrays (SoA)
|
||||
# currently used.
|
||||
cdef:
|
||||
intp_t pivot_idx, i, store_idx
|
||||
floating pivot_val
|
||||
|
||||
# in the small-array case, do things efficiently
|
||||
if size <= 1:
|
||||
pass
|
||||
elif size == 2:
|
||||
if values[0] > values[1]:
|
||||
dual_swap(values, indices, 0, 1)
|
||||
elif size == 3:
|
||||
if values[0] > values[1]:
|
||||
dual_swap(values, indices, 0, 1)
|
||||
if values[1] > values[2]:
|
||||
dual_swap(values, indices, 1, 2)
|
||||
if values[0] > values[1]:
|
||||
dual_swap(values, indices, 0, 1)
|
||||
else:
|
||||
# Determine the pivot using the median-of-three rule.
|
||||
# The smallest of the three is moved to the beginning of the array,
|
||||
# the middle (the pivot value) is moved to the end, and the largest
|
||||
# is moved to the pivot index.
|
||||
pivot_idx = size // 2
|
||||
if values[0] > values[size - 1]:
|
||||
dual_swap(values, indices, 0, size - 1)
|
||||
if values[size - 1] > values[pivot_idx]:
|
||||
dual_swap(values, indices, size - 1, pivot_idx)
|
||||
if values[0] > values[size - 1]:
|
||||
dual_swap(values, indices, 0, size - 1)
|
||||
pivot_val = values[size - 1]
|
||||
|
||||
# Partition indices about pivot. At the end of this operation,
|
||||
# pivot_idx will contain the pivot value, everything to the left
|
||||
# will be smaller, and everything to the right will be larger.
|
||||
store_idx = 0
|
||||
for i in range(size - 1):
|
||||
if values[i] < pivot_val:
|
||||
dual_swap(values, indices, i, store_idx)
|
||||
store_idx += 1
|
||||
dual_swap(values, indices, store_idx, size - 1)
|
||||
pivot_idx = store_idx
|
||||
|
||||
# Recursively sort each side of the pivot
|
||||
if pivot_idx > 1:
|
||||
simultaneous_sort(values, indices, pivot_idx)
|
||||
if pivot_idx + 2 < size:
|
||||
simultaneous_sort(values + pivot_idx + 1,
|
||||
indices + pivot_idx + 1,
|
||||
size - pivot_idx - 1)
|
||||
return 0
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
# Commonly used types
|
||||
# These are redefinitions of the ones defined by numpy in
|
||||
# https://github.com/numpy/numpy/blob/main/numpy/__init__.pxd.
|
||||
# It will eventually avoid having to always include the numpy headers even when we
|
||||
# would only use it for the types.
|
||||
#
|
||||
# When used to declare variables that will receive values from numpy arrays, it
|
||||
# should match the dtype of the array. For example, to declare a variable that will
|
||||
# receive values from a numpy array of dtype np.float64, the type float64_t must be
|
||||
# used.
|
||||
#
|
||||
# TODO: Stop defining custom types locally or globally like DTYPE_t and friends and
|
||||
# use these consistently throughout the codebase.
|
||||
# NOTE: Extend this list as needed when converting more cython extensions.
|
||||
ctypedef unsigned char uint8_t
|
||||
ctypedef unsigned int uint32_t
|
||||
ctypedef unsigned long long uint64_t
|
||||
# Note: In NumPy 2, indexing always happens with npy_intp which is an alias for
|
||||
# the Py_ssize_t type, see PEP 353.
|
||||
#
|
||||
# Note that on most platforms Py_ssize_t is equivalent to C99's intptr_t,
|
||||
# but they can differ on architecture with segmented memory (none
|
||||
# supported by scikit-learn at the time of writing).
|
||||
#
|
||||
# intp_t/np.intp should be used to index arrays in a platform dependent way.
|
||||
# Storing arrays with platform dependent dtypes as attribute on picklable
|
||||
# objects is not recommended as it requires special care when loading and
|
||||
# using such datastructures on a host with different bitness. Instead one
|
||||
# should rather use fixed width integer types such as int32 or uint32 when we know
|
||||
# that the number of elements to index is not larger to 2 or 4 billions.
|
||||
ctypedef Py_ssize_t intp_t
|
||||
ctypedef float float32_t
|
||||
ctypedef double float64_t
|
||||
# Sparse matrices indices and indices' pointers arrays must use int32_t over
|
||||
# intp_t because intp_t is platform dependent.
|
||||
# When large sparse matrices are supported, indexing must use int64_t.
|
||||
# See https://github.com/scikit-learn/scikit-learn/issues/23653 which tracks the
|
||||
# ongoing work to support large sparse matrices.
|
||||
ctypedef signed char int8_t
|
||||
ctypedef signed int int32_t
|
||||
ctypedef signed long long int64_t
|
||||
# Commonly used types
|
||||
# These are redefinitions of the ones defined by numpy in
|
||||
# https://github.com/numpy/numpy/blob/main/numpy/__init__.pxd.
|
||||
# It will eventually avoid having to always include the numpy headers even when we
|
||||
# would only use it for the types.
|
||||
#
|
||||
# When used to declare variables that will receive values from numpy arrays, it
|
||||
# should match the dtype of the array. For example, to declare a variable that will
|
||||
# receive values from a numpy array of dtype np.float64, the type float64_t must be
|
||||
# used.
|
||||
#
|
||||
# TODO: Stop defining custom types locally or globally like DTYPE_t and friends and
|
||||
# use these consistently throughout the codebase.
|
||||
# NOTE: Extend this list as needed when converting more cython extensions.
|
||||
ctypedef unsigned char uint8_t
|
||||
ctypedef unsigned int uint32_t
|
||||
ctypedef unsigned long long uint64_t
|
||||
# Note: In NumPy 2, indexing always happens with npy_intp which is an alias for
|
||||
# the Py_ssize_t type, see PEP 353.
|
||||
#
|
||||
# Note that on most platforms Py_ssize_t is equivalent to C99's intptr_t,
|
||||
# but they can differ on architecture with segmented memory (none
|
||||
# supported by scikit-learn at the time of writing).
|
||||
#
|
||||
# intp_t/np.intp should be used to index arrays in a platform dependent way.
|
||||
# Storing arrays with platform dependent dtypes as attribute on picklable
|
||||
# objects is not recommended as it requires special care when loading and
|
||||
# using such datastructures on a host with different bitness. Instead one
|
||||
# should rather use fixed width integer types such as int32 or uint32 when we know
|
||||
# that the number of elements to index is not larger to 2 or 4 billions.
|
||||
ctypedef Py_ssize_t intp_t
|
||||
ctypedef float float32_t
|
||||
ctypedef double float64_t
|
||||
# Sparse matrices indices and indices' pointers arrays must use int32_t over
|
||||
# intp_t because intp_t is platform dependent.
|
||||
# When large sparse matrices are supported, indexing must use int64_t.
|
||||
# See https://github.com/scikit-learn/scikit-learn/issues/23653 which tracks the
|
||||
# ongoing work to support large sparse matrices.
|
||||
ctypedef signed char int8_t
|
||||
ctypedef signed int int32_t
|
||||
ctypedef signed long long int64_t
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
# _typedefs is a declaration only module
|
||||
#
|
||||
# The functions implemented here are for testing purpose only.
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
ctypedef fused testing_type_t:
|
||||
float32_t
|
||||
float64_t
|
||||
int8_t
|
||||
int32_t
|
||||
int64_t
|
||||
intp_t
|
||||
uint8_t
|
||||
uint32_t
|
||||
uint64_t
|
||||
|
||||
|
||||
def testing_make_array_from_typed_val(testing_type_t val):
|
||||
cdef testing_type_t[:] val_view = <testing_type_t[:1]>&val
|
||||
return np.asarray(val_view)
|
||||
# _typedefs is a declaration only module
|
||||
#
|
||||
# The functions implemented here are for testing purpose only.
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
ctypedef fused testing_type_t:
|
||||
float32_t
|
||||
float64_t
|
||||
int8_t
|
||||
int32_t
|
||||
int64_t
|
||||
intp_t
|
||||
uint8_t
|
||||
uint32_t
|
||||
uint64_t
|
||||
|
||||
|
||||
def testing_make_array_from_typed_val(testing_type_t val):
|
||||
cdef testing_type_t[:] val_view = <testing_type_t[:1]>&val
|
||||
return np.asarray(val_view)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
cimport numpy as cnp
|
||||
|
||||
from libcpp.vector cimport vector
|
||||
from ..utils._typedefs cimport intp_t, float64_t, int32_t, int64_t
|
||||
|
||||
ctypedef fused vector_typed:
|
||||
vector[float64_t]
|
||||
vector[intp_t]
|
||||
vector[int32_t]
|
||||
vector[int64_t]
|
||||
|
||||
cdef cnp.ndarray vector_to_nd_array(vector_typed * vect_ptr)
|
||||
cimport numpy as cnp
|
||||
|
||||
from libcpp.vector cimport vector
|
||||
from ..utils._typedefs cimport intp_t, float64_t, int32_t, int64_t
|
||||
|
||||
ctypedef fused vector_typed:
|
||||
vector[float64_t]
|
||||
vector[intp_t]
|
||||
vector[int32_t]
|
||||
vector[int64_t]
|
||||
|
||||
cdef cnp.ndarray vector_to_nd_array(vector_typed * vect_ptr)
|
||||
|
||||
@@ -1,118 +1,118 @@
|
||||
from cython.operator cimport dereference as deref
|
||||
from cpython.ref cimport Py_INCREF
|
||||
cimport numpy as cnp
|
||||
|
||||
cnp.import_array()
|
||||
|
||||
|
||||
cdef StdVectorSentinel _create_sentinel(vector_typed * vect_ptr):
|
||||
if vector_typed is vector[float64_t]:
|
||||
return StdVectorSentinelFloat64.create_for(vect_ptr)
|
||||
elif vector_typed is vector[int32_t]:
|
||||
return StdVectorSentinelInt32.create_for(vect_ptr)
|
||||
elif vector_typed is vector[int64_t]:
|
||||
return StdVectorSentinelInt64.create_for(vect_ptr)
|
||||
else: # intp_t
|
||||
return StdVectorSentinelIntP.create_for(vect_ptr)
|
||||
|
||||
|
||||
cdef class StdVectorSentinel:
|
||||
"""Wraps a reference to a vector which will be deallocated with this object.
|
||||
|
||||
When created, the StdVectorSentinel swaps the reference of its internal
|
||||
vectors with the provided one (vect_ptr), thus making the StdVectorSentinel
|
||||
manage the provided one's lifetime.
|
||||
"""
|
||||
cdef void* get_data(self):
|
||||
"""Return pointer to data."""
|
||||
|
||||
cdef int get_typenum(self):
|
||||
"""Get typenum for PyArray_SimpleNewFromData."""
|
||||
|
||||
|
||||
cdef class StdVectorSentinelFloat64(StdVectorSentinel):
|
||||
cdef vector[float64_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[float64_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelFloat64 sentinel = StdVectorSentinelFloat64.__new__(StdVectorSentinelFloat64)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_FLOAT64
|
||||
|
||||
|
||||
cdef class StdVectorSentinelIntP(StdVectorSentinel):
|
||||
cdef vector[intp_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[intp_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelIntP sentinel = StdVectorSentinelIntP.__new__(StdVectorSentinelIntP)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_INTP
|
||||
|
||||
|
||||
cdef class StdVectorSentinelInt32(StdVectorSentinel):
|
||||
cdef vector[int32_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[int32_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelInt32 sentinel = StdVectorSentinelInt32.__new__(StdVectorSentinelInt32)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_INT32
|
||||
|
||||
|
||||
cdef class StdVectorSentinelInt64(StdVectorSentinel):
|
||||
cdef vector[int64_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[int64_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelInt64 sentinel = StdVectorSentinelInt64.__new__(StdVectorSentinelInt64)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_INT64
|
||||
|
||||
|
||||
cdef cnp.ndarray vector_to_nd_array(vector_typed * vect_ptr):
|
||||
cdef:
|
||||
cnp.npy_intp size = deref(vect_ptr).size()
|
||||
StdVectorSentinel sentinel = _create_sentinel(vect_ptr)
|
||||
cnp.ndarray arr = cnp.PyArray_SimpleNewFromData(
|
||||
1, &size, sentinel.get_typenum(), sentinel.get_data())
|
||||
|
||||
# Makes the numpy array responsible of the life-cycle of its buffer.
|
||||
# A reference to the StdVectorSentinel will be stolen by the call to
|
||||
# `PyArray_SetBaseObject` below, so we increase its reference counter.
|
||||
# See: https://docs.python.org/3/c-api/intro.html#reference-count-details
|
||||
Py_INCREF(sentinel)
|
||||
cnp.PyArray_SetBaseObject(arr, sentinel)
|
||||
return arr
|
||||
from cython.operator cimport dereference as deref
|
||||
from cpython.ref cimport Py_INCREF
|
||||
cimport numpy as cnp
|
||||
|
||||
cnp.import_array()
|
||||
|
||||
|
||||
cdef StdVectorSentinel _create_sentinel(vector_typed * vect_ptr):
|
||||
if vector_typed is vector[float64_t]:
|
||||
return StdVectorSentinelFloat64.create_for(vect_ptr)
|
||||
elif vector_typed is vector[int32_t]:
|
||||
return StdVectorSentinelInt32.create_for(vect_ptr)
|
||||
elif vector_typed is vector[int64_t]:
|
||||
return StdVectorSentinelInt64.create_for(vect_ptr)
|
||||
else: # intp_t
|
||||
return StdVectorSentinelIntP.create_for(vect_ptr)
|
||||
|
||||
|
||||
cdef class StdVectorSentinel:
|
||||
"""Wraps a reference to a vector which will be deallocated with this object.
|
||||
|
||||
When created, the StdVectorSentinel swaps the reference of its internal
|
||||
vectors with the provided one (vect_ptr), thus making the StdVectorSentinel
|
||||
manage the provided one's lifetime.
|
||||
"""
|
||||
cdef void* get_data(self):
|
||||
"""Return pointer to data."""
|
||||
|
||||
cdef int get_typenum(self):
|
||||
"""Get typenum for PyArray_SimpleNewFromData."""
|
||||
|
||||
|
||||
cdef class StdVectorSentinelFloat64(StdVectorSentinel):
|
||||
cdef vector[float64_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[float64_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelFloat64 sentinel = StdVectorSentinelFloat64.__new__(StdVectorSentinelFloat64)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_FLOAT64
|
||||
|
||||
|
||||
cdef class StdVectorSentinelIntP(StdVectorSentinel):
|
||||
cdef vector[intp_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[intp_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelIntP sentinel = StdVectorSentinelIntP.__new__(StdVectorSentinelIntP)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_INTP
|
||||
|
||||
|
||||
cdef class StdVectorSentinelInt32(StdVectorSentinel):
|
||||
cdef vector[int32_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[int32_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelInt32 sentinel = StdVectorSentinelInt32.__new__(StdVectorSentinelInt32)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_INT32
|
||||
|
||||
|
||||
cdef class StdVectorSentinelInt64(StdVectorSentinel):
|
||||
cdef vector[int64_t] vec
|
||||
|
||||
@staticmethod
|
||||
cdef StdVectorSentinel create_for(vector[int64_t] * vect_ptr):
|
||||
# This initializes the object directly without calling __init__
|
||||
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
|
||||
cdef StdVectorSentinelInt64 sentinel = StdVectorSentinelInt64.__new__(StdVectorSentinelInt64)
|
||||
sentinel.vec.swap(deref(vect_ptr))
|
||||
return sentinel
|
||||
|
||||
cdef void* get_data(self):
|
||||
return self.vec.data()
|
||||
|
||||
cdef int get_typenum(self):
|
||||
return cnp.NPY_INT64
|
||||
|
||||
|
||||
cdef cnp.ndarray vector_to_nd_array(vector_typed * vect_ptr):
|
||||
cdef:
|
||||
cnp.npy_intp size = deref(vect_ptr).size()
|
||||
StdVectorSentinel sentinel = _create_sentinel(vect_ptr)
|
||||
cnp.ndarray arr = cnp.PyArray_SimpleNewFromData(
|
||||
1, &size, sentinel.get_typenum(), sentinel.get_data())
|
||||
|
||||
# Makes the numpy array responsible of the life-cycle of its buffer.
|
||||
# A reference to the StdVectorSentinel will be stolen by the call to
|
||||
# `PyArray_SetBaseObject` below, so we increase its reference counter.
|
||||
# See: https://docs.python.org/3/c-api/intro.html#reference-count-details
|
||||
Py_INCREF(sentinel)
|
||||
cnp.PyArray_SetBaseObject(arr, sentinel)
|
||||
return arr
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Efficient (dense) parameter vector implementation for linear models.
|
||||
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: weight_vector.pxd
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# name_suffix, c_type
|
||||
dtypes = [('64', 'double'),
|
||||
('32', 'float')]
|
||||
|
||||
}}
|
||||
|
||||
{{for name_suffix, c_type in dtypes}}
|
||||
|
||||
cdef class WeightVector{{name_suffix}}(object):
|
||||
cdef readonly {{c_type}}[::1] w
|
||||
cdef readonly {{c_type}}[::1] aw
|
||||
cdef {{c_type}} *w_data_ptr
|
||||
cdef {{c_type}} *aw_data_ptr
|
||||
|
||||
cdef double wscale
|
||||
cdef double average_a
|
||||
cdef double average_b
|
||||
cdef int n_features
|
||||
cdef double sq_norm
|
||||
|
||||
cdef void add(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz, {{c_type}} c) noexcept nogil
|
||||
cdef void add_average(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz, {{c_type}} c, {{c_type}} num_iter) noexcept nogil
|
||||
cdef {{c_type}} dot(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz) noexcept nogil
|
||||
cdef void scale(self, {{c_type}} c) noexcept nogil
|
||||
cdef void reset_wscale(self) noexcept nogil
|
||||
cdef {{c_type}} norm(self) noexcept nogil
|
||||
|
||||
{{endfor}}
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Efficient (dense) parameter vector implementation for linear models.
|
||||
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: weight_vector.pxd
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# name_suffix, c_type
|
||||
dtypes = [('64', 'double'),
|
||||
('32', 'float')]
|
||||
|
||||
}}
|
||||
|
||||
{{for name_suffix, c_type in dtypes}}
|
||||
|
||||
cdef class WeightVector{{name_suffix}}(object):
|
||||
cdef readonly {{c_type}}[::1] w
|
||||
cdef readonly {{c_type}}[::1] aw
|
||||
cdef {{c_type}} *w_data_ptr
|
||||
cdef {{c_type}} *aw_data_ptr
|
||||
|
||||
cdef double wscale
|
||||
cdef double average_a
|
||||
cdef double average_b
|
||||
cdef int n_features
|
||||
cdef double sq_norm
|
||||
|
||||
cdef void add(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz, {{c_type}} c) noexcept nogil
|
||||
cdef void add_average(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz, {{c_type}} c, {{c_type}} num_iter) noexcept nogil
|
||||
cdef {{c_type}} dot(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz) noexcept nogil
|
||||
cdef void scale(self, {{c_type}} c) noexcept nogil
|
||||
cdef void reset_wscale(self) noexcept nogil
|
||||
cdef {{c_type}} norm(self) noexcept nogil
|
||||
|
||||
{{endfor}}
|
||||
|
||||
@@ -1,207 +1,207 @@
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Efficient (dense) parameter vector implementation for linear models.
|
||||
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: weight_vector.pxd
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# name_suffix, c_type, reset_wscale_threshold
|
||||
dtypes = [('64', 'double', 1e-9),
|
||||
('32', 'float', 1e-6)]
|
||||
|
||||
}}
|
||||
|
||||
# cython: binding=False
|
||||
#
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cimport cython
|
||||
from libc.limits cimport INT_MAX
|
||||
from libc.math cimport sqrt
|
||||
|
||||
from ._cython_blas cimport _dot, _scal, _axpy
|
||||
|
||||
{{for name_suffix, c_type, reset_wscale_threshold in dtypes}}
|
||||
|
||||
cdef class WeightVector{{name_suffix}}(object):
|
||||
"""Dense vector represented by a scalar and a numpy array.
|
||||
|
||||
The class provides methods to ``add`` a sparse vector
|
||||
and scale the vector.
|
||||
Representing a vector explicitly as a scalar times a
|
||||
vector allows for efficient scaling operations.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
w : ndarray, dtype={{c_type}}, order='C'
|
||||
The numpy array which backs the weight vector.
|
||||
aw : ndarray, dtype={{c_type}}, order='C'
|
||||
The numpy array which backs the average_weight vector.
|
||||
w_data_ptr : {{c_type}}*
|
||||
A pointer to the data of the numpy array.
|
||||
wscale : {{c_type}}
|
||||
The scale of the vector.
|
||||
n_features : int
|
||||
The number of features (= dimensionality of ``w``).
|
||||
sq_norm : {{c_type}}
|
||||
The squared norm of ``w``.
|
||||
"""
|
||||
|
||||
def __cinit__(self,
|
||||
{{c_type}}[::1] w,
|
||||
{{c_type}}[::1] aw):
|
||||
|
||||
if w.shape[0] > INT_MAX:
|
||||
raise ValueError("More than %d features not supported; got %d."
|
||||
% (INT_MAX, w.shape[0]))
|
||||
self.w = w
|
||||
self.w_data_ptr = &w[0]
|
||||
self.wscale = 1.0
|
||||
self.n_features = w.shape[0]
|
||||
self.sq_norm = _dot(self.n_features, self.w_data_ptr, 1, self.w_data_ptr, 1)
|
||||
|
||||
self.aw = aw
|
||||
if self.aw is not None:
|
||||
self.aw_data_ptr = &aw[0]
|
||||
self.average_a = 0.0
|
||||
self.average_b = 1.0
|
||||
|
||||
cdef void add(self, {{c_type}} *x_data_ptr, int *x_ind_ptr, int xnnz,
|
||||
{{c_type}} c) noexcept nogil:
|
||||
"""Scales sample x by constant c and adds it to the weight vector.
|
||||
|
||||
This operation updates ``sq_norm``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}*
|
||||
The array which holds the feature values of ``x``.
|
||||
x_ind_ptr : np.intc*
|
||||
The array which holds the feature indices of ``x``.
|
||||
xnnz : int
|
||||
The number of non-zero features of ``x``.
|
||||
c : {{c_type}}
|
||||
The scaling constant for the example.
|
||||
"""
|
||||
cdef int j
|
||||
cdef int idx
|
||||
cdef double val
|
||||
cdef double innerprod = 0.0
|
||||
cdef double xsqnorm = 0.0
|
||||
|
||||
# the next two lines save a factor of 2!
|
||||
cdef {{c_type}} wscale = self.wscale
|
||||
cdef {{c_type}}* w_data_ptr = self.w_data_ptr
|
||||
|
||||
for j in range(xnnz):
|
||||
idx = x_ind_ptr[j]
|
||||
val = x_data_ptr[j]
|
||||
innerprod += (w_data_ptr[idx] * val)
|
||||
xsqnorm += (val * val)
|
||||
w_data_ptr[idx] += val * (c / wscale)
|
||||
|
||||
self.sq_norm += (xsqnorm * c * c) + (2.0 * innerprod * wscale * c)
|
||||
|
||||
# Update the average weights according to the sparse trick defined
|
||||
# here: https://research.microsoft.com/pubs/192769/tricks-2012.pdf
|
||||
# by Leon Bottou
|
||||
cdef void add_average(self, {{c_type}} *x_data_ptr, int *x_ind_ptr, int xnnz,
|
||||
{{c_type}} c, {{c_type}} num_iter) noexcept nogil:
|
||||
"""Updates the average weight vector.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}*
|
||||
The array which holds the feature values of ``x``.
|
||||
x_ind_ptr : np.intc*
|
||||
The array which holds the feature indices of ``x``.
|
||||
xnnz : int
|
||||
The number of non-zero features of ``x``.
|
||||
c : {{c_type}}
|
||||
The scaling constant for the example.
|
||||
num_iter : {{c_type}}
|
||||
The total number of iterations.
|
||||
"""
|
||||
cdef int j
|
||||
cdef int idx
|
||||
cdef double val
|
||||
cdef double mu = 1.0 / num_iter
|
||||
cdef double average_a = self.average_a
|
||||
cdef double wscale = self.wscale
|
||||
cdef {{c_type}}* aw_data_ptr = self.aw_data_ptr
|
||||
|
||||
for j in range(xnnz):
|
||||
idx = x_ind_ptr[j]
|
||||
val = x_data_ptr[j]
|
||||
aw_data_ptr[idx] += (self.average_a * val * (-c / wscale))
|
||||
|
||||
# Once the sample has been processed
|
||||
# update the average_a and average_b
|
||||
if num_iter > 1:
|
||||
self.average_b /= (1.0 - mu)
|
||||
self.average_a += mu * self.average_b * wscale
|
||||
|
||||
cdef {{c_type}} dot(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz) noexcept nogil:
|
||||
"""Computes the dot product of a sample x and the weight vector.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}*
|
||||
The array which holds the feature values of ``x``.
|
||||
x_ind_ptr : np.intc*
|
||||
The array which holds the feature indices of ``x``.
|
||||
xnnz : int
|
||||
The number of non-zero features of ``x`` (length of x_ind_ptr).
|
||||
|
||||
Returns
|
||||
-------
|
||||
innerprod : {{c_type}}
|
||||
The inner product of ``x`` and ``w``.
|
||||
"""
|
||||
cdef int j
|
||||
cdef int idx
|
||||
cdef double innerprod = 0.0
|
||||
cdef {{c_type}}* w_data_ptr = self.w_data_ptr
|
||||
for j in range(xnnz):
|
||||
idx = x_ind_ptr[j]
|
||||
innerprod += w_data_ptr[idx] * x_data_ptr[j]
|
||||
innerprod *= self.wscale
|
||||
return innerprod
|
||||
|
||||
cdef void scale(self, {{c_type}} c) noexcept nogil:
|
||||
"""Scales the weight vector by a constant ``c``.
|
||||
|
||||
It updates ``wscale`` and ``sq_norm``. If ``wscale`` gets too
|
||||
small we call ``reset_swcale``."""
|
||||
self.wscale *= c
|
||||
self.sq_norm *= (c * c)
|
||||
|
||||
if self.wscale < {{reset_wscale_threshold}}:
|
||||
self.reset_wscale()
|
||||
|
||||
cdef void reset_wscale(self) noexcept nogil:
|
||||
"""Scales each coef of ``w`` by ``wscale`` and resets it to 1. """
|
||||
if self.aw_data_ptr != NULL:
|
||||
_axpy(self.n_features, self.average_a,
|
||||
self.w_data_ptr, 1, self.aw_data_ptr, 1)
|
||||
_scal(self.n_features, 1.0 / self.average_b, self.aw_data_ptr, 1)
|
||||
self.average_a = 0.0
|
||||
self.average_b = 1.0
|
||||
|
||||
_scal(self.n_features, self.wscale, self.w_data_ptr, 1)
|
||||
self.wscale = 1.0
|
||||
|
||||
cdef {{c_type}} norm(self) noexcept nogil:
|
||||
"""The L2 norm of the weight vector. """
|
||||
return sqrt(self.sq_norm)
|
||||
|
||||
{{endfor}}
|
||||
{{py:
|
||||
|
||||
"""
|
||||
Efficient (dense) parameter vector implementation for linear models.
|
||||
|
||||
Template file for easily generate fused types consistent code using Tempita
|
||||
(https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
|
||||
|
||||
Generated file: weight_vector.pxd
|
||||
|
||||
Each class is duplicated for all dtypes (float and double). The keywords
|
||||
between double braces are substituted during the build.
|
||||
"""
|
||||
|
||||
# name_suffix, c_type, reset_wscale_threshold
|
||||
dtypes = [('64', 'double', 1e-9),
|
||||
('32', 'float', 1e-6)]
|
||||
|
||||
}}
|
||||
|
||||
# cython: binding=False
|
||||
#
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cimport cython
|
||||
from libc.limits cimport INT_MAX
|
||||
from libc.math cimport sqrt
|
||||
|
||||
from ._cython_blas cimport _dot, _scal, _axpy
|
||||
|
||||
{{for name_suffix, c_type, reset_wscale_threshold in dtypes}}
|
||||
|
||||
cdef class WeightVector{{name_suffix}}(object):
|
||||
"""Dense vector represented by a scalar and a numpy array.
|
||||
|
||||
The class provides methods to ``add`` a sparse vector
|
||||
and scale the vector.
|
||||
Representing a vector explicitly as a scalar times a
|
||||
vector allows for efficient scaling operations.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
w : ndarray, dtype={{c_type}}, order='C'
|
||||
The numpy array which backs the weight vector.
|
||||
aw : ndarray, dtype={{c_type}}, order='C'
|
||||
The numpy array which backs the average_weight vector.
|
||||
w_data_ptr : {{c_type}}*
|
||||
A pointer to the data of the numpy array.
|
||||
wscale : {{c_type}}
|
||||
The scale of the vector.
|
||||
n_features : int
|
||||
The number of features (= dimensionality of ``w``).
|
||||
sq_norm : {{c_type}}
|
||||
The squared norm of ``w``.
|
||||
"""
|
||||
|
||||
def __cinit__(self,
|
||||
{{c_type}}[::1] w,
|
||||
{{c_type}}[::1] aw):
|
||||
|
||||
if w.shape[0] > INT_MAX:
|
||||
raise ValueError("More than %d features not supported; got %d."
|
||||
% (INT_MAX, w.shape[0]))
|
||||
self.w = w
|
||||
self.w_data_ptr = &w[0]
|
||||
self.wscale = 1.0
|
||||
self.n_features = w.shape[0]
|
||||
self.sq_norm = _dot(self.n_features, self.w_data_ptr, 1, self.w_data_ptr, 1)
|
||||
|
||||
self.aw = aw
|
||||
if self.aw is not None:
|
||||
self.aw_data_ptr = &aw[0]
|
||||
self.average_a = 0.0
|
||||
self.average_b = 1.0
|
||||
|
||||
cdef void add(self, {{c_type}} *x_data_ptr, int *x_ind_ptr, int xnnz,
|
||||
{{c_type}} c) noexcept nogil:
|
||||
"""Scales sample x by constant c and adds it to the weight vector.
|
||||
|
||||
This operation updates ``sq_norm``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}*
|
||||
The array which holds the feature values of ``x``.
|
||||
x_ind_ptr : np.intc*
|
||||
The array which holds the feature indices of ``x``.
|
||||
xnnz : int
|
||||
The number of non-zero features of ``x``.
|
||||
c : {{c_type}}
|
||||
The scaling constant for the example.
|
||||
"""
|
||||
cdef int j
|
||||
cdef int idx
|
||||
cdef double val
|
||||
cdef double innerprod = 0.0
|
||||
cdef double xsqnorm = 0.0
|
||||
|
||||
# the next two lines save a factor of 2!
|
||||
cdef {{c_type}} wscale = self.wscale
|
||||
cdef {{c_type}}* w_data_ptr = self.w_data_ptr
|
||||
|
||||
for j in range(xnnz):
|
||||
idx = x_ind_ptr[j]
|
||||
val = x_data_ptr[j]
|
||||
innerprod += (w_data_ptr[idx] * val)
|
||||
xsqnorm += (val * val)
|
||||
w_data_ptr[idx] += val * (c / wscale)
|
||||
|
||||
self.sq_norm += (xsqnorm * c * c) + (2.0 * innerprod * wscale * c)
|
||||
|
||||
# Update the average weights according to the sparse trick defined
|
||||
# here: https://research.microsoft.com/pubs/192769/tricks-2012.pdf
|
||||
# by Leon Bottou
|
||||
cdef void add_average(self, {{c_type}} *x_data_ptr, int *x_ind_ptr, int xnnz,
|
||||
{{c_type}} c, {{c_type}} num_iter) noexcept nogil:
|
||||
"""Updates the average weight vector.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}*
|
||||
The array which holds the feature values of ``x``.
|
||||
x_ind_ptr : np.intc*
|
||||
The array which holds the feature indices of ``x``.
|
||||
xnnz : int
|
||||
The number of non-zero features of ``x``.
|
||||
c : {{c_type}}
|
||||
The scaling constant for the example.
|
||||
num_iter : {{c_type}}
|
||||
The total number of iterations.
|
||||
"""
|
||||
cdef int j
|
||||
cdef int idx
|
||||
cdef double val
|
||||
cdef double mu = 1.0 / num_iter
|
||||
cdef double average_a = self.average_a
|
||||
cdef double wscale = self.wscale
|
||||
cdef {{c_type}}* aw_data_ptr = self.aw_data_ptr
|
||||
|
||||
for j in range(xnnz):
|
||||
idx = x_ind_ptr[j]
|
||||
val = x_data_ptr[j]
|
||||
aw_data_ptr[idx] += (self.average_a * val * (-c / wscale))
|
||||
|
||||
# Once the sample has been processed
|
||||
# update the average_a and average_b
|
||||
if num_iter > 1:
|
||||
self.average_b /= (1.0 - mu)
|
||||
self.average_a += mu * self.average_b * wscale
|
||||
|
||||
cdef {{c_type}} dot(self, {{c_type}} *x_data_ptr, int *x_ind_ptr,
|
||||
int xnnz) noexcept nogil:
|
||||
"""Computes the dot product of a sample x and the weight vector.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x_data_ptr : {{c_type}}*
|
||||
The array which holds the feature values of ``x``.
|
||||
x_ind_ptr : np.intc*
|
||||
The array which holds the feature indices of ``x``.
|
||||
xnnz : int
|
||||
The number of non-zero features of ``x`` (length of x_ind_ptr).
|
||||
|
||||
Returns
|
||||
-------
|
||||
innerprod : {{c_type}}
|
||||
The inner product of ``x`` and ``w``.
|
||||
"""
|
||||
cdef int j
|
||||
cdef int idx
|
||||
cdef double innerprod = 0.0
|
||||
cdef {{c_type}}* w_data_ptr = self.w_data_ptr
|
||||
for j in range(xnnz):
|
||||
idx = x_ind_ptr[j]
|
||||
innerprod += w_data_ptr[idx] * x_data_ptr[j]
|
||||
innerprod *= self.wscale
|
||||
return innerprod
|
||||
|
||||
cdef void scale(self, {{c_type}} c) noexcept nogil:
|
||||
"""Scales the weight vector by a constant ``c``.
|
||||
|
||||
It updates ``wscale`` and ``sq_norm``. If ``wscale`` gets too
|
||||
small we call ``reset_swcale``."""
|
||||
self.wscale *= c
|
||||
self.sq_norm *= (c * c)
|
||||
|
||||
if self.wscale < {{reset_wscale_threshold}}:
|
||||
self.reset_wscale()
|
||||
|
||||
cdef void reset_wscale(self) noexcept nogil:
|
||||
"""Scales each coef of ``w`` by ``wscale`` and resets it to 1. """
|
||||
if self.aw_data_ptr != NULL:
|
||||
_axpy(self.n_features, self.average_a,
|
||||
self.w_data_ptr, 1, self.aw_data_ptr, 1)
|
||||
_scal(self.n_features, 1.0 / self.average_b, self.aw_data_ptr, 1)
|
||||
self.average_a = 0.0
|
||||
self.average_b = 1.0
|
||||
|
||||
_scal(self.n_features, self.wscale, self.w_data_ptr, 1)
|
||||
self.wscale = 1.0
|
||||
|
||||
cdef {{c_type}} norm(self) noexcept nogil:
|
||||
"""The L2 norm of the weight vector. """
|
||||
return sqrt(self.sq_norm)
|
||||
|
||||
{{endfor}}
|
||||
|
||||
@@ -1,118 +1,118 @@
|
||||
"""A small collection of auxiliary functions that operate on arrays."""
|
||||
|
||||
from cython cimport floating
|
||||
from libc.math cimport fabs
|
||||
from libc.float cimport DBL_MAX, FLT_MAX
|
||||
|
||||
from ._cython_blas cimport _copy, _rotg, _rot
|
||||
|
||||
|
||||
ctypedef fused real_numeric:
|
||||
short
|
||||
int
|
||||
long
|
||||
long long
|
||||
float
|
||||
double
|
||||
|
||||
|
||||
def min_pos(const floating[:] X):
|
||||
"""Find the minimum value of an array over positive values.
|
||||
|
||||
Returns the maximum representable value of the input dtype if none of the
|
||||
values are positive.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : ndarray of shape (n,)
|
||||
Input array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
min_val : float
|
||||
The smallest positive value in the array, or the maximum representable value
|
||||
of the input dtype if no positive values are found.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from sklearn.utils.arrayfuncs import min_pos
|
||||
>>> X = np.array([0, -1, 2, 3, -4, 5])
|
||||
>>> min_pos(X)
|
||||
2.0
|
||||
"""
|
||||
cdef Py_ssize_t i
|
||||
cdef floating min_val = FLT_MAX if floating is float else DBL_MAX
|
||||
for i in range(X.size):
|
||||
if 0. < X[i] < min_val:
|
||||
min_val = X[i]
|
||||
return min_val
|
||||
|
||||
|
||||
def _all_with_any_reduction_axis_1(real_numeric[:, :] array, real_numeric value):
|
||||
"""Check whether any row contains all values equal to `value`.
|
||||
|
||||
It is equivalent to `np.any(np.all(X == value, axis=1))`, but it avoids to
|
||||
materialize the temporary boolean matrices in memory.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
array: array-like
|
||||
The array to be checked.
|
||||
value: short, int, long, float, or double
|
||||
The value to use for the comparison.
|
||||
|
||||
Returns
|
||||
-------
|
||||
any_all_equal: bool
|
||||
Whether or not any rows contains all values equal to `value`.
|
||||
"""
|
||||
cdef Py_ssize_t i, j
|
||||
|
||||
for i in range(array.shape[0]):
|
||||
for j in range(array.shape[1]):
|
||||
if array[i, j] != value:
|
||||
break
|
||||
else: # no break
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# General Cholesky Delete.
|
||||
# Remove an element from the cholesky factorization
|
||||
# m = columns
|
||||
# n = rows
|
||||
#
|
||||
# TODO: put transpose as an option
|
||||
def cholesky_delete(floating[:, :] L, int go_out):
|
||||
cdef:
|
||||
int n = L.shape[0]
|
||||
int m = L.strides[0]
|
||||
floating c, s
|
||||
floating *L1
|
||||
int i
|
||||
|
||||
if floating is float:
|
||||
m /= sizeof(float)
|
||||
else:
|
||||
m /= sizeof(double)
|
||||
|
||||
# delete row go_out
|
||||
L1 = &L[0, 0] + (go_out * m)
|
||||
for i in range(go_out, n-1):
|
||||
_copy(i + 2, L1 + m, 1, L1, 1)
|
||||
L1 += m
|
||||
|
||||
L1 = &L[0, 0] + (go_out * m)
|
||||
for i in range(go_out, n-1):
|
||||
_rotg(L1 + i, L1 + i + 1, &c, &s)
|
||||
if L1[i] < 0:
|
||||
# Diagonals cannot be negative
|
||||
L1[i] = fabs(L1[i])
|
||||
c = -c
|
||||
s = -s
|
||||
|
||||
L1[i + 1] = 0. # just for cleanup
|
||||
L1 += m
|
||||
|
||||
_rot(n - i - 2, L1 + i, m, L1 + i + 1, m, c, s)
|
||||
"""A small collection of auxiliary functions that operate on arrays."""
|
||||
|
||||
from cython cimport floating
|
||||
from libc.math cimport fabs
|
||||
from libc.float cimport DBL_MAX, FLT_MAX
|
||||
|
||||
from ._cython_blas cimport _copy, _rotg, _rot
|
||||
|
||||
|
||||
ctypedef fused real_numeric:
|
||||
short
|
||||
int
|
||||
long
|
||||
long long
|
||||
float
|
||||
double
|
||||
|
||||
|
||||
def min_pos(const floating[:] X):
|
||||
"""Find the minimum value of an array over positive values.
|
||||
|
||||
Returns the maximum representable value of the input dtype if none of the
|
||||
values are positive.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : ndarray of shape (n,)
|
||||
Input array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
min_val : float
|
||||
The smallest positive value in the array, or the maximum representable value
|
||||
of the input dtype if no positive values are found.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from sklearn.utils.arrayfuncs import min_pos
|
||||
>>> X = np.array([0, -1, 2, 3, -4, 5])
|
||||
>>> min_pos(X)
|
||||
2.0
|
||||
"""
|
||||
cdef Py_ssize_t i
|
||||
cdef floating min_val = FLT_MAX if floating is float else DBL_MAX
|
||||
for i in range(X.size):
|
||||
if 0. < X[i] < min_val:
|
||||
min_val = X[i]
|
||||
return min_val
|
||||
|
||||
|
||||
def _all_with_any_reduction_axis_1(real_numeric[:, :] array, real_numeric value):
|
||||
"""Check whether any row contains all values equal to `value`.
|
||||
|
||||
It is equivalent to `np.any(np.all(X == value, axis=1))`, but it avoids to
|
||||
materialize the temporary boolean matrices in memory.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
array: array-like
|
||||
The array to be checked.
|
||||
value: short, int, long, float, or double
|
||||
The value to use for the comparison.
|
||||
|
||||
Returns
|
||||
-------
|
||||
any_all_equal: bool
|
||||
Whether or not any rows contains all values equal to `value`.
|
||||
"""
|
||||
cdef Py_ssize_t i, j
|
||||
|
||||
for i in range(array.shape[0]):
|
||||
for j in range(array.shape[1]):
|
||||
if array[i, j] != value:
|
||||
break
|
||||
else: # no break
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# General Cholesky Delete.
|
||||
# Remove an element from the cholesky factorization
|
||||
# m = columns
|
||||
# n = rows
|
||||
#
|
||||
# TODO: put transpose as an option
|
||||
def cholesky_delete(floating[:, :] L, int go_out):
|
||||
cdef:
|
||||
int n = L.shape[0]
|
||||
int m = L.strides[0]
|
||||
floating c, s
|
||||
floating *L1
|
||||
int i
|
||||
|
||||
if floating is float:
|
||||
m /= sizeof(float)
|
||||
else:
|
||||
m /= sizeof(double)
|
||||
|
||||
# delete row go_out
|
||||
L1 = &L[0, 0] + (go_out * m)
|
||||
for i in range(go_out, n-1):
|
||||
_copy(i + 2, L1 + m, 1, L1, 1)
|
||||
L1 += m
|
||||
|
||||
L1 = &L[0, 0] + (go_out * m)
|
||||
for i in range(go_out, n-1):
|
||||
_rotg(L1 + i, L1 + i + 1, &c, &s)
|
||||
if L1[i] < 0:
|
||||
# Diagonals cannot be negative
|
||||
L1[i] = fabs(L1[i])
|
||||
c = -c
|
||||
s = -s
|
||||
|
||||
L1[i + 1] = 0. # just for cleanup
|
||||
L1 += m
|
||||
|
||||
_rot(n - i - 2, L1 + i, m, L1 + i + 1, m, c, s)
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
# utils is cimported from other subpackages so this is needed for the cimport
|
||||
# to work
|
||||
utils_cython_tree = [
|
||||
# We add sklearn_root_cython_tree to make sure sklearn/__init__.py is copied
|
||||
# early in the build
|
||||
sklearn_root_cython_tree,
|
||||
fs.copyfile('__init__.py'),
|
||||
fs.copyfile('_cython_blas.pxd'),
|
||||
fs.copyfile('_heap.pxd'),
|
||||
fs.copyfile('_openmp_helpers.pxd'),
|
||||
fs.copyfile('_random.pxd'),
|
||||
fs.copyfile('_sorting.pxd'),
|
||||
fs.copyfile('_typedefs.pxd'),
|
||||
fs.copyfile('_vector_sentinel.pxd'),
|
||||
]
|
||||
|
||||
utils_extension_metadata = {
|
||||
'sparsefuncs_fast':
|
||||
{'sources': [cython_gen.process('sparsefuncs_fast.pyx')]},
|
||||
'_cython_blas': {'sources': [cython_gen.process('_cython_blas.pyx')]},
|
||||
'arrayfuncs': {'sources': [cython_gen.process('arrayfuncs.pyx')]},
|
||||
'murmurhash': {
|
||||
'sources': [cython_gen.process('murmurhash.pyx'), 'src' / 'MurmurHash3.cpp'],
|
||||
},
|
||||
'_fast_dict':
|
||||
{'sources': [cython_gen_cpp.process('_fast_dict.pyx')]},
|
||||
'_openmp_helpers': {'sources': [cython_gen.process('_openmp_helpers.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_random': {'sources': [cython_gen.process('_random.pyx')]},
|
||||
'_typedefs': {'sources': [cython_gen.process('_typedefs.pyx')]},
|
||||
'_heap': {'sources': [cython_gen.process('_heap.pyx')]},
|
||||
'_sorting': {'sources': [cython_gen.process('_sorting.pyx')]},
|
||||
'_vector_sentinel':
|
||||
{'sources': [cython_gen_cpp.process('_vector_sentinel.pyx')],
|
||||
'dependencies': [np_dep]},
|
||||
'_isfinite': {'sources': [cython_gen.process('_isfinite.pyx')]},
|
||||
}
|
||||
|
||||
foreach ext_name, ext_dict : utils_extension_metadata
|
||||
py.extension_module(
|
||||
ext_name,
|
||||
[ext_dict.get('sources'), utils_cython_tree],
|
||||
dependencies: ext_dict.get('dependencies', []),
|
||||
subdir: 'sklearn/utils',
|
||||
install: true
|
||||
)
|
||||
endforeach
|
||||
|
||||
util_extension_names = ['_seq_dataset', '_weight_vector']
|
||||
|
||||
foreach name: util_extension_names
|
||||
pxd = custom_target(
|
||||
name + '_pxd',
|
||||
output: name + '.pxd',
|
||||
input: name + '.pxd.tp',
|
||||
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
|
||||
)
|
||||
utils_cython_tree += [pxd]
|
||||
|
||||
pyx = custom_target(
|
||||
name + '_pyx',
|
||||
output: name + '.pyx',
|
||||
input: name + '.pyx.tp',
|
||||
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
|
||||
# TODO in principle this should go in py.exension_module below. This is
|
||||
# temporary work-around for dependency issue with .pyx.tp files. For more
|
||||
# details, see https://github.com/mesonbuild/meson/issues/13212
|
||||
depends: [pxd, utils_cython_tree],
|
||||
)
|
||||
py.extension_module(
|
||||
name,
|
||||
cython_gen.process(pyx),
|
||||
subdir: 'sklearn/utils',
|
||||
install: true
|
||||
)
|
||||
endforeach
|
||||
# utils is cimported from other subpackages so this is needed for the cimport
|
||||
# to work
|
||||
utils_cython_tree = [
|
||||
# We add sklearn_root_cython_tree to make sure sklearn/__init__.py is copied
|
||||
# early in the build
|
||||
sklearn_root_cython_tree,
|
||||
fs.copyfile('__init__.py'),
|
||||
fs.copyfile('_cython_blas.pxd'),
|
||||
fs.copyfile('_heap.pxd'),
|
||||
fs.copyfile('_openmp_helpers.pxd'),
|
||||
fs.copyfile('_random.pxd'),
|
||||
fs.copyfile('_sorting.pxd'),
|
||||
fs.copyfile('_typedefs.pxd'),
|
||||
fs.copyfile('_vector_sentinel.pxd'),
|
||||
]
|
||||
|
||||
utils_extension_metadata = {
|
||||
'sparsefuncs_fast':
|
||||
{'sources': [cython_gen.process('sparsefuncs_fast.pyx')]},
|
||||
'_cython_blas': {'sources': [cython_gen.process('_cython_blas.pyx')]},
|
||||
'arrayfuncs': {'sources': [cython_gen.process('arrayfuncs.pyx')]},
|
||||
'murmurhash': {
|
||||
'sources': [cython_gen.process('murmurhash.pyx'), 'src' / 'MurmurHash3.cpp'],
|
||||
},
|
||||
'_fast_dict':
|
||||
{'sources': [cython_gen_cpp.process('_fast_dict.pyx')]},
|
||||
'_openmp_helpers': {'sources': [cython_gen.process('_openmp_helpers.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_random': {'sources': [cython_gen.process('_random.pyx')]},
|
||||
'_typedefs': {'sources': [cython_gen.process('_typedefs.pyx')]},
|
||||
'_heap': {'sources': [cython_gen.process('_heap.pyx')]},
|
||||
'_sorting': {'sources': [cython_gen.process('_sorting.pyx')]},
|
||||
'_vector_sentinel':
|
||||
{'sources': [cython_gen_cpp.process('_vector_sentinel.pyx')],
|
||||
'dependencies': [np_dep]},
|
||||
'_isfinite': {'sources': [cython_gen.process('_isfinite.pyx')]},
|
||||
}
|
||||
|
||||
foreach ext_name, ext_dict : utils_extension_metadata
|
||||
py.extension_module(
|
||||
ext_name,
|
||||
[ext_dict.get('sources'), utils_cython_tree],
|
||||
dependencies: ext_dict.get('dependencies', []),
|
||||
subdir: 'sklearn/utils',
|
||||
install: true
|
||||
)
|
||||
endforeach
|
||||
|
||||
util_extension_names = ['_seq_dataset', '_weight_vector']
|
||||
|
||||
foreach name: util_extension_names
|
||||
pxd = custom_target(
|
||||
name + '_pxd',
|
||||
output: name + '.pxd',
|
||||
input: name + '.pxd.tp',
|
||||
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
|
||||
)
|
||||
utils_cython_tree += [pxd]
|
||||
|
||||
pyx = custom_target(
|
||||
name + '_pyx',
|
||||
output: name + '.pyx',
|
||||
input: name + '.pyx.tp',
|
||||
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
|
||||
# TODO in principle this should go in py.exension_module below. This is
|
||||
# temporary work-around for dependency issue with .pyx.tp files. For more
|
||||
# details, see https://github.com/mesonbuild/meson/issues/13212
|
||||
depends: [pxd, utils_cython_tree],
|
||||
)
|
||||
py.extension_module(
|
||||
name,
|
||||
cython_gen.process(pyx),
|
||||
subdir: 'sklearn/utils',
|
||||
install: true
|
||||
)
|
||||
endforeach
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
"""Export fast murmurhash C/C++ routines + cython wrappers"""
|
||||
|
||||
from ..utils._typedefs cimport int32_t, uint32_t
|
||||
|
||||
# The C API is disabled for now, since it requires -I flags to get
|
||||
# compilation to work even when these functions are not used.
|
||||
# cdef extern from "MurmurHash3.h":
|
||||
# void MurmurHash3_x86_32(void* key, int len, unsigned int seed,
|
||||
# void* out)
|
||||
#
|
||||
# void MurmurHash3_x86_128(void* key, int len, unsigned int seed,
|
||||
# void* out)
|
||||
#
|
||||
# void MurmurHash3_x64_128(void* key, int len, unsigned int seed,
|
||||
# void* out)
|
||||
|
||||
|
||||
cpdef uint32_t murmurhash3_int_u32(int key, unsigned int seed)
|
||||
cpdef int32_t murmurhash3_int_s32(int key, unsigned int seed)
|
||||
cpdef uint32_t murmurhash3_bytes_u32(bytes key, unsigned int seed)
|
||||
cpdef int32_t murmurhash3_bytes_s32(bytes key, unsigned int seed)
|
||||
"""Export fast murmurhash C/C++ routines + cython wrappers"""
|
||||
|
||||
from ..utils._typedefs cimport int32_t, uint32_t
|
||||
|
||||
# The C API is disabled for now, since it requires -I flags to get
|
||||
# compilation to work even when these functions are not used.
|
||||
# cdef extern from "MurmurHash3.h":
|
||||
# void MurmurHash3_x86_32(void* key, int len, unsigned int seed,
|
||||
# void* out)
|
||||
#
|
||||
# void MurmurHash3_x86_128(void* key, int len, unsigned int seed,
|
||||
# void* out)
|
||||
#
|
||||
# void MurmurHash3_x64_128(void* key, int len, unsigned int seed,
|
||||
# void* out)
|
||||
|
||||
|
||||
cpdef uint32_t murmurhash3_int_u32(int key, unsigned int seed)
|
||||
cpdef int32_t murmurhash3_int_s32(int key, unsigned int seed)
|
||||
cpdef uint32_t murmurhash3_bytes_u32(bytes key, unsigned int seed)
|
||||
cpdef int32_t murmurhash3_bytes_s32(bytes key, unsigned int seed)
|
||||
|
||||
@@ -1,135 +1,135 @@
|
||||
"""Cython wrapper for MurmurHash3 non-cryptographic hash function.
|
||||
|
||||
MurmurHash is an extensively tested and very fast hash function that has
|
||||
good distribution properties suitable for machine learning use cases
|
||||
such as feature hashing and random projections.
|
||||
|
||||
The original C++ code by Austin Appleby is released the public domain
|
||||
and can be found here:
|
||||
|
||||
https://code.google.com/p/smhasher/
|
||||
|
||||
"""
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from ..utils._typedefs cimport int32_t, uint32_t
|
||||
|
||||
import numpy as np
|
||||
|
||||
cdef extern from "src/MurmurHash3.h":
|
||||
void MurmurHash3_x86_32(void *key, int len, uint32_t seed, void *out)
|
||||
void MurmurHash3_x86_128(void *key, int len, uint32_t seed, void *out)
|
||||
void MurmurHash3_x64_128 (void *key, int len, uint32_t seed, void *out)
|
||||
|
||||
|
||||
cpdef uint32_t murmurhash3_int_u32(int key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a int key at seed."""
|
||||
cdef uint32_t out
|
||||
MurmurHash3_x86_32(&key, sizeof(int), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
cpdef int32_t murmurhash3_int_s32(int key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a int key at seed."""
|
||||
cdef int32_t out
|
||||
MurmurHash3_x86_32(&key, sizeof(int), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
cpdef uint32_t murmurhash3_bytes_u32(bytes key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a bytes key at seed."""
|
||||
cdef uint32_t out
|
||||
MurmurHash3_x86_32(<char*> key, len(key), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
cpdef int32_t murmurhash3_bytes_s32(bytes key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a bytes key at seed."""
|
||||
cdef int32_t out
|
||||
MurmurHash3_x86_32(<char*> key, len(key), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
def _murmurhash3_bytes_array_u32(
|
||||
const int32_t[:] key,
|
||||
unsigned int seed,
|
||||
):
|
||||
"""Compute 32bit murmurhash3 hashes of a key int array at seed."""
|
||||
# TODO make it possible to pass preallocated output array
|
||||
cdef:
|
||||
uint32_t[:] out = np.zeros(key.size, np.uint32)
|
||||
Py_ssize_t i
|
||||
for i in range(key.shape[0]):
|
||||
out[i] = murmurhash3_int_u32(key[i], seed)
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
def _murmurhash3_bytes_array_s32(
|
||||
const int32_t[:] key,
|
||||
unsigned int seed,
|
||||
):
|
||||
"""Compute 32bit murmurhash3 hashes of a key int array at seed."""
|
||||
# TODO make it possible to pass preallocated output array
|
||||
cdef:
|
||||
int32_t[:] out = np.zeros(key.size, np.int32)
|
||||
Py_ssize_t i
|
||||
for i in range(key.shape[0]):
|
||||
out[i] = murmurhash3_int_s32(key[i], seed)
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
def murmurhash3_32(key, seed=0, positive=False):
|
||||
"""Compute the 32bit murmurhash3 of key at seed.
|
||||
|
||||
The underlying implementation is MurmurHash3_x86_32 generating low
|
||||
latency 32bits hash suitable for implementing lookup tables, Bloom
|
||||
filters, count min sketch or feature hashing.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key : np.int32, bytes, unicode or ndarray of dtype=np.int32
|
||||
The physical object to hash.
|
||||
|
||||
seed : int, default=0
|
||||
Integer seed for the hashing algorithm.
|
||||
|
||||
positive : bool, default=False
|
||||
True: the results is casted to an unsigned int
|
||||
from 0 to 2 ** 32 - 1
|
||||
False: the results is casted to a signed int
|
||||
from -(2 ** 31) to 2 ** 31 - 1
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from sklearn.utils import murmurhash3_32
|
||||
>>> murmurhash3_32(b"Hello World!", seed=42)
|
||||
3565178
|
||||
"""
|
||||
if isinstance(key, bytes):
|
||||
if positive:
|
||||
return murmurhash3_bytes_u32(key, seed)
|
||||
else:
|
||||
return murmurhash3_bytes_s32(key, seed)
|
||||
elif isinstance(key, unicode):
|
||||
if positive:
|
||||
return murmurhash3_bytes_u32(key.encode('utf-8'), seed)
|
||||
else:
|
||||
return murmurhash3_bytes_s32(key.encode('utf-8'), seed)
|
||||
elif isinstance(key, int) or isinstance(key, np.int32):
|
||||
if positive:
|
||||
return murmurhash3_int_u32(<int32_t>key, seed)
|
||||
else:
|
||||
return murmurhash3_int_s32(<int32_t>key, seed)
|
||||
elif isinstance(key, np.ndarray):
|
||||
if key.dtype != np.int32:
|
||||
raise TypeError(
|
||||
"key.dtype should be int32, got %s" % key.dtype)
|
||||
if positive:
|
||||
return _murmurhash3_bytes_array_u32(key.ravel(), seed).reshape(key.shape)
|
||||
else:
|
||||
return _murmurhash3_bytes_array_s32(key.ravel(), seed).reshape(key.shape)
|
||||
else:
|
||||
raise TypeError(
|
||||
"key %r with type %s is not supported. "
|
||||
"Explicit conversion to bytes is required" % (key, type(key)))
|
||||
"""Cython wrapper for MurmurHash3 non-cryptographic hash function.
|
||||
|
||||
MurmurHash is an extensively tested and very fast hash function that has
|
||||
good distribution properties suitable for machine learning use cases
|
||||
such as feature hashing and random projections.
|
||||
|
||||
The original C++ code by Austin Appleby is released the public domain
|
||||
and can be found here:
|
||||
|
||||
https://code.google.com/p/smhasher/
|
||||
|
||||
"""
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from ..utils._typedefs cimport int32_t, uint32_t
|
||||
|
||||
import numpy as np
|
||||
|
||||
cdef extern from "src/MurmurHash3.h":
|
||||
void MurmurHash3_x86_32(void *key, int len, uint32_t seed, void *out)
|
||||
void MurmurHash3_x86_128(void *key, int len, uint32_t seed, void *out)
|
||||
void MurmurHash3_x64_128 (void *key, int len, uint32_t seed, void *out)
|
||||
|
||||
|
||||
cpdef uint32_t murmurhash3_int_u32(int key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a int key at seed."""
|
||||
cdef uint32_t out
|
||||
MurmurHash3_x86_32(&key, sizeof(int), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
cpdef int32_t murmurhash3_int_s32(int key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a int key at seed."""
|
||||
cdef int32_t out
|
||||
MurmurHash3_x86_32(&key, sizeof(int), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
cpdef uint32_t murmurhash3_bytes_u32(bytes key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a bytes key at seed."""
|
||||
cdef uint32_t out
|
||||
MurmurHash3_x86_32(<char*> key, len(key), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
cpdef int32_t murmurhash3_bytes_s32(bytes key, unsigned int seed):
|
||||
"""Compute the 32bit murmurhash3 of a bytes key at seed."""
|
||||
cdef int32_t out
|
||||
MurmurHash3_x86_32(<char*> key, len(key), seed, &out)
|
||||
return out
|
||||
|
||||
|
||||
def _murmurhash3_bytes_array_u32(
|
||||
const int32_t[:] key,
|
||||
unsigned int seed,
|
||||
):
|
||||
"""Compute 32bit murmurhash3 hashes of a key int array at seed."""
|
||||
# TODO make it possible to pass preallocated output array
|
||||
cdef:
|
||||
uint32_t[:] out = np.zeros(key.size, np.uint32)
|
||||
Py_ssize_t i
|
||||
for i in range(key.shape[0]):
|
||||
out[i] = murmurhash3_int_u32(key[i], seed)
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
def _murmurhash3_bytes_array_s32(
|
||||
const int32_t[:] key,
|
||||
unsigned int seed,
|
||||
):
|
||||
"""Compute 32bit murmurhash3 hashes of a key int array at seed."""
|
||||
# TODO make it possible to pass preallocated output array
|
||||
cdef:
|
||||
int32_t[:] out = np.zeros(key.size, np.int32)
|
||||
Py_ssize_t i
|
||||
for i in range(key.shape[0]):
|
||||
out[i] = murmurhash3_int_s32(key[i], seed)
|
||||
return np.asarray(out)
|
||||
|
||||
|
||||
def murmurhash3_32(key, seed=0, positive=False):
|
||||
"""Compute the 32bit murmurhash3 of key at seed.
|
||||
|
||||
The underlying implementation is MurmurHash3_x86_32 generating low
|
||||
latency 32bits hash suitable for implementing lookup tables, Bloom
|
||||
filters, count min sketch or feature hashing.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key : np.int32, bytes, unicode or ndarray of dtype=np.int32
|
||||
The physical object to hash.
|
||||
|
||||
seed : int, default=0
|
||||
Integer seed for the hashing algorithm.
|
||||
|
||||
positive : bool, default=False
|
||||
True: the results is casted to an unsigned int
|
||||
from 0 to 2 ** 32 - 1
|
||||
False: the results is casted to a signed int
|
||||
from -(2 ** 31) to 2 ** 31 - 1
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from sklearn.utils import murmurhash3_32
|
||||
>>> murmurhash3_32(b"Hello World!", seed=42)
|
||||
3565178
|
||||
"""
|
||||
if isinstance(key, bytes):
|
||||
if positive:
|
||||
return murmurhash3_bytes_u32(key, seed)
|
||||
else:
|
||||
return murmurhash3_bytes_s32(key, seed)
|
||||
elif isinstance(key, unicode):
|
||||
if positive:
|
||||
return murmurhash3_bytes_u32(key.encode('utf-8'), seed)
|
||||
else:
|
||||
return murmurhash3_bytes_s32(key.encode('utf-8'), seed)
|
||||
elif isinstance(key, int) or isinstance(key, np.int32):
|
||||
if positive:
|
||||
return murmurhash3_int_u32(<int32_t>key, seed)
|
||||
else:
|
||||
return murmurhash3_int_s32(<int32_t>key, seed)
|
||||
elif isinstance(key, np.ndarray):
|
||||
if key.dtype != np.int32:
|
||||
raise TypeError(
|
||||
"key.dtype should be int32, got %s" % key.dtype)
|
||||
if positive:
|
||||
return _murmurhash3_bytes_array_u32(key.ravel(), seed).reshape(key.shape)
|
||||
else:
|
||||
return _murmurhash3_bytes_array_s32(key.ravel(), seed).reshape(key.shape)
|
||||
else:
|
||||
raise TypeError(
|
||||
"key %r with type %s is not supported. "
|
||||
"Explicit conversion to bytes is required" % (key, type(key)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,345 +1,345 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||
// algorithms are optimized for their respective platforms. You can still
|
||||
// compile and run any of them on any platform, but your performance with the
|
||||
// non-native version will be less than optimal.
|
||||
|
||||
#include "MurmurHash3.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform-specific functions and macros
|
||||
|
||||
// Microsoft Visual Studio
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#define FORCE_INLINE __forceinline
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ROTL32(x,y) _rotl(x,y)
|
||||
#define ROTL64(x,y) _rotl64(x,y)
|
||||
|
||||
#define BIG_CONSTANT(x) (x)
|
||||
|
||||
// Other compilers
|
||||
|
||||
#else // defined(_MSC_VER)
|
||||
|
||||
#if defined(GNUC) && ((GNUC > 4) || (GNUC == 4 && GNUC_MINOR >= 4))
|
||||
|
||||
/* gcc version >= 4.4 4.1 = RHEL 5, 4.4 = RHEL 6.
|
||||
* Don't inline for RHEL 5 gcc which is 4.1 */
|
||||
#define FORCE_INLINE attribute((always_inline))
|
||||
|
||||
#else
|
||||
|
||||
#define FORCE_INLINE
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
inline uint32_t rotl32 ( uint32_t x, int8_t r )
|
||||
{
|
||||
return (x << r) | (x >> (32 - r));
|
||||
}
|
||||
|
||||
inline uint64_t rotl64 ( uint64_t x, int8_t r )
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
#define ROTL32(x,y) rotl32(x,y)
|
||||
#define ROTL64(x,y) rotl64(x,y)
|
||||
|
||||
#define BIG_CONSTANT(x) (x##LLU)
|
||||
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Block read - if your platform needs to do endian-swapping or can only
|
||||
// handle aligned reads, do the conversion here
|
||||
|
||||
FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i )
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
FORCE_INLINE uint64_t getblock ( const uint64_t * p, int i )
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finalization mix - force all bits of a hash block to avalanche
|
||||
|
||||
FORCE_INLINE uint32_t fmix ( uint32_t h )
|
||||
{
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
//----------
|
||||
|
||||
FORCE_INLINE uint64_t fmix ( uint64_t k )
|
||||
{
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
|
||||
k ^= k >> 33;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_32 ( const void * key, int len,
|
||||
uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 4;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
|
||||
uint32_t c1 = 0xcc9e2d51;
|
||||
uint32_t c2 = 0x1b873593;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
|
||||
|
||||
for(int i = -nblocks; i; i++)
|
||||
{
|
||||
uint32_t k1 = getblock(blocks,i);
|
||||
|
||||
k1 *= c1;
|
||||
k1 = ROTL32(k1,15);
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = ROTL32(h1,13);
|
||||
h1 = h1*5+0xe6546b64;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
|
||||
switch(len & 3)
|
||||
{
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 1: k1 ^= tail[0];
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len;
|
||||
|
||||
h1 = fmix(h1);
|
||||
|
||||
*(uint32_t*)out = h1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_128 ( const void * key, const int len,
|
||||
uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
uint32_t h2 = seed;
|
||||
uint32_t h3 = seed;
|
||||
uint32_t h4 = seed;
|
||||
|
||||
uint32_t c1 = 0x239b961b;
|
||||
uint32_t c2 = 0xab0e9789;
|
||||
uint32_t c3 = 0x38b34ae5;
|
||||
uint32_t c4 = 0xa1e38b93;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
|
||||
|
||||
for(int i = -nblocks; i; i++)
|
||||
{
|
||||
uint32_t k1 = getblock(blocks,i*4+0);
|
||||
uint32_t k2 = getblock(blocks,i*4+1);
|
||||
uint32_t k3 = getblock(blocks,i*4+2);
|
||||
uint32_t k4 = getblock(blocks,i*4+3);
|
||||
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
|
||||
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
|
||||
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
|
||||
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
uint32_t k2 = 0;
|
||||
uint32_t k3 = 0;
|
||||
uint32_t k4 = 0;
|
||||
|
||||
switch(len & 15)
|
||||
{
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = fmix(h1);
|
||||
h2 = fmix(h2);
|
||||
h3 = fmix(h3);
|
||||
h4 = fmix(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
((uint32_t*)out)[0] = h1;
|
||||
((uint32_t*)out)[1] = h2;
|
||||
((uint32_t*)out)[2] = h3;
|
||||
((uint32_t*)out)[3] = h4;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x64_128 ( const void * key, const int len,
|
||||
const uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint64_t h1 = seed;
|
||||
uint64_t h2 = seed;
|
||||
|
||||
uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
|
||||
uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint64_t * blocks = (const uint64_t *)(data);
|
||||
|
||||
for(int i = 0; i < nblocks; i++)
|
||||
{
|
||||
uint64_t k1 = getblock(blocks,i*2+0);
|
||||
uint64_t k2 = getblock(blocks,i*2+1);
|
||||
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
|
||||
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
||||
|
||||
uint64_t k1 = 0;
|
||||
uint64_t k2 = 0;
|
||||
|
||||
switch(len & 15)
|
||||
{
|
||||
case 15: k2 ^= uint64_t(tail[14]) << 48;
|
||||
case 14: k2 ^= uint64_t(tail[13]) << 40;
|
||||
case 13: k2 ^= uint64_t(tail[12]) << 32;
|
||||
case 12: k2 ^= uint64_t(tail[11]) << 24;
|
||||
case 11: k2 ^= uint64_t(tail[10]) << 16;
|
||||
case 10: k2 ^= uint64_t(tail[ 9]) << 8;
|
||||
case 9: k2 ^= uint64_t(tail[ 8]) << 0;
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
case 8: k1 ^= uint64_t(tail[ 7]) << 56;
|
||||
case 7: k1 ^= uint64_t(tail[ 6]) << 48;
|
||||
case 6: k1 ^= uint64_t(tail[ 5]) << 40;
|
||||
case 5: k1 ^= uint64_t(tail[ 4]) << 32;
|
||||
case 4: k1 ^= uint64_t(tail[ 3]) << 24;
|
||||
case 3: k1 ^= uint64_t(tail[ 2]) << 16;
|
||||
case 2: k1 ^= uint64_t(tail[ 1]) << 8;
|
||||
case 1: k1 ^= uint64_t(tail[ 0]) << 0;
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix(h1);
|
||||
h2 = fmix(h2);
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
((uint64_t*)out)[0] = h1;
|
||||
((uint64_t*)out)[1] = h2;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||
// algorithms are optimized for their respective platforms. You can still
|
||||
// compile and run any of them on any platform, but your performance with the
|
||||
// non-native version will be less than optimal.
|
||||
|
||||
#include "MurmurHash3.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform-specific functions and macros
|
||||
|
||||
// Microsoft Visual Studio
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#define FORCE_INLINE __forceinline
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ROTL32(x,y) _rotl(x,y)
|
||||
#define ROTL64(x,y) _rotl64(x,y)
|
||||
|
||||
#define BIG_CONSTANT(x) (x)
|
||||
|
||||
// Other compilers
|
||||
|
||||
#else // defined(_MSC_VER)
|
||||
|
||||
#if defined(GNUC) && ((GNUC > 4) || (GNUC == 4 && GNUC_MINOR >= 4))
|
||||
|
||||
/* gcc version >= 4.4 4.1 = RHEL 5, 4.4 = RHEL 6.
|
||||
* Don't inline for RHEL 5 gcc which is 4.1 */
|
||||
#define FORCE_INLINE attribute((always_inline))
|
||||
|
||||
#else
|
||||
|
||||
#define FORCE_INLINE
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
inline uint32_t rotl32 ( uint32_t x, int8_t r )
|
||||
{
|
||||
return (x << r) | (x >> (32 - r));
|
||||
}
|
||||
|
||||
inline uint64_t rotl64 ( uint64_t x, int8_t r )
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
#define ROTL32(x,y) rotl32(x,y)
|
||||
#define ROTL64(x,y) rotl64(x,y)
|
||||
|
||||
#define BIG_CONSTANT(x) (x##LLU)
|
||||
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Block read - if your platform needs to do endian-swapping or can only
|
||||
// handle aligned reads, do the conversion here
|
||||
|
||||
FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i )
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
FORCE_INLINE uint64_t getblock ( const uint64_t * p, int i )
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finalization mix - force all bits of a hash block to avalanche
|
||||
|
||||
FORCE_INLINE uint32_t fmix ( uint32_t h )
|
||||
{
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
//----------
|
||||
|
||||
FORCE_INLINE uint64_t fmix ( uint64_t k )
|
||||
{
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
|
||||
k ^= k >> 33;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_32 ( const void * key, int len,
|
||||
uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 4;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
|
||||
uint32_t c1 = 0xcc9e2d51;
|
||||
uint32_t c2 = 0x1b873593;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
|
||||
|
||||
for(int i = -nblocks; i; i++)
|
||||
{
|
||||
uint32_t k1 = getblock(blocks,i);
|
||||
|
||||
k1 *= c1;
|
||||
k1 = ROTL32(k1,15);
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = ROTL32(h1,13);
|
||||
h1 = h1*5+0xe6546b64;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
|
||||
switch(len & 3)
|
||||
{
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 1: k1 ^= tail[0];
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len;
|
||||
|
||||
h1 = fmix(h1);
|
||||
|
||||
*(uint32_t*)out = h1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_128 ( const void * key, const int len,
|
||||
uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
uint32_t h2 = seed;
|
||||
uint32_t h3 = seed;
|
||||
uint32_t h4 = seed;
|
||||
|
||||
uint32_t c1 = 0x239b961b;
|
||||
uint32_t c2 = 0xab0e9789;
|
||||
uint32_t c3 = 0x38b34ae5;
|
||||
uint32_t c4 = 0xa1e38b93;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
|
||||
|
||||
for(int i = -nblocks; i; i++)
|
||||
{
|
||||
uint32_t k1 = getblock(blocks,i*4+0);
|
||||
uint32_t k2 = getblock(blocks,i*4+1);
|
||||
uint32_t k3 = getblock(blocks,i*4+2);
|
||||
uint32_t k4 = getblock(blocks,i*4+3);
|
||||
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
|
||||
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
|
||||
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
|
||||
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
uint32_t k2 = 0;
|
||||
uint32_t k3 = 0;
|
||||
uint32_t k4 = 0;
|
||||
|
||||
switch(len & 15)
|
||||
{
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = fmix(h1);
|
||||
h2 = fmix(h2);
|
||||
h3 = fmix(h3);
|
||||
h4 = fmix(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
((uint32_t*)out)[0] = h1;
|
||||
((uint32_t*)out)[1] = h2;
|
||||
((uint32_t*)out)[2] = h3;
|
||||
((uint32_t*)out)[3] = h4;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x64_128 ( const void * key, const int len,
|
||||
const uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint64_t h1 = seed;
|
||||
uint64_t h2 = seed;
|
||||
|
||||
uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
|
||||
uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint64_t * blocks = (const uint64_t *)(data);
|
||||
|
||||
for(int i = 0; i < nblocks; i++)
|
||||
{
|
||||
uint64_t k1 = getblock(blocks,i*2+0);
|
||||
uint64_t k2 = getblock(blocks,i*2+1);
|
||||
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
|
||||
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
||||
|
||||
uint64_t k1 = 0;
|
||||
uint64_t k2 = 0;
|
||||
|
||||
switch(len & 15)
|
||||
{
|
||||
case 15: k2 ^= uint64_t(tail[14]) << 48;
|
||||
case 14: k2 ^= uint64_t(tail[13]) << 40;
|
||||
case 13: k2 ^= uint64_t(tail[12]) << 32;
|
||||
case 12: k2 ^= uint64_t(tail[11]) << 24;
|
||||
case 11: k2 ^= uint64_t(tail[10]) << 16;
|
||||
case 10: k2 ^= uint64_t(tail[ 9]) << 8;
|
||||
case 9: k2 ^= uint64_t(tail[ 8]) << 0;
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
case 8: k1 ^= uint64_t(tail[ 7]) << 56;
|
||||
case 7: k1 ^= uint64_t(tail[ 6]) << 48;
|
||||
case 6: k1 ^= uint64_t(tail[ 5]) << 40;
|
||||
case 5: k1 ^= uint64_t(tail[ 4]) << 32;
|
||||
case 4: k1 ^= uint64_t(tail[ 3]) << 24;
|
||||
case 3: k1 ^= uint64_t(tail[ 2]) << 16;
|
||||
case 2: k1 ^= uint64_t(tail[ 1]) << 8;
|
||||
case 1: k1 ^= uint64_t(tail[ 0]) << 0;
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix(h1);
|
||||
h2 = fmix(h2);
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
((uint64_t*)out)[0] = h1;
|
||||
((uint64_t*)out)[1] = h2;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
#ifndef _MURMURHASH3_H_
|
||||
#define _MURMURHASH3_H_
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform-specific functions and macros
|
||||
|
||||
// Microsoft Visual Studio
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned long uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
// Other compilers
|
||||
|
||||
#else // defined(_MSC_VER)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // _MURMURHASH3_H_
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
#ifndef _MURMURHASH3_H_
|
||||
#define _MURMURHASH3_H_
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform-specific functions and macros
|
||||
|
||||
// Microsoft Visual Studio
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned long uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
// Other compilers
|
||||
|
||||
#else // defined(_MSC_VER)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // _MURMURHASH3_H_
|
||||
|
||||
Reference in New Issue
Block a user