Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b51fbb0

Browse files
authored
MAINT Refactor vector sentinel into utils (#22728)
1 parent cbe5d4a commit b51fbb0

File tree

4 files changed

+99
-65
lines changed

4 files changed

+99
-65
lines changed

sklearn/metrics/_pairwise_distances_reduction.pyx

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ from ..utils._heap cimport heap_push
4242
from ..utils._sorting cimport simultaneous_sort
4343
from ..utils._openmp_helpers cimport _openmp_thread_num
4444
from ..utils._typedefs cimport ITYPE_t, DTYPE_t
45-
from ..utils._typedefs cimport ITYPECODE, DTYPECODE
45+
from ..utils._vector_sentinel cimport vector_to_nd_array
4646

4747
from numbers import Integral, Real
4848
from typing import List
@@ -76,70 +76,6 @@ ctypedef fused vector_vector_DITYPE_t:
7676
vector[vector[DTYPE_t]]
7777

7878

79-
cdef class StdVectorSentinel:
80-
"""Wraps a reference to a vector which will be deallocated with this object.
81-
82-
When created, the StdVectorSentinel swaps the reference of its internal
83-
vectors with the provided one (vec_ptr), thus making the StdVectorSentinel
84-
manage the provided one's lifetime.
85-
"""
86-
pass
87-
88-
89-
# We necessarily need to define two extension types extending StdVectorSentinel
90-
# because we need to provide the dtype of the vector but can't use numeric fused types.
91-
cdef class StdVectorSentinelDTYPE(StdVectorSentinel):
92-
cdef vector[DTYPE_t] vec
93-
94-
@staticmethod
95-
cdef StdVectorSentinel create_for(vector[DTYPE_t] * vec_ptr):
96-
# This initializes the object directly without calling __init__
97-
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
98-
cdef StdVectorSentinelDTYPE sentinel = StdVectorSentinelDTYPE.__new__(StdVectorSentinelDTYPE)
99-
sentinel.vec.swap(deref(vec_ptr))
100-
return sentinel
101-
102-
103-
cdef class StdVectorSentinelITYPE(StdVectorSentinel):
104-
cdef vector[ITYPE_t] vec
105-
106-
@staticmethod
107-
cdef StdVectorSentinel create_for(vector[ITYPE_t] * vec_ptr):
108-
# This initializes the object directly without calling __init__
109-
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
110-
cdef StdVectorSentinelITYPE sentinel = StdVectorSentinelITYPE.__new__(StdVectorSentinelITYPE)
111-
sentinel.vec.swap(deref(vec_ptr))
112-
return sentinel
113-
114-
115-
cdef np.ndarray vector_to_nd_array(vector_DITYPE_t * vect_ptr):
116-
"""Create a numpy ndarray given a C++ vector.
117-
118-
The numpy array buffer is the one of the C++ vector.
119-
A StdVectorSentinel is registered as the base object for the numpy array,
120-
freeing the C++ vector it encapsulates when the numpy array is freed.
121-
"""
122-
typenum = DTYPECODE if vector_DITYPE_t is vector[DTYPE_t] else ITYPECODE
123-
cdef:
124-
np.npy_intp size = deref(vect_ptr).size()
125-
np.ndarray arr = np.PyArray_SimpleNewFromData(1, &size, typenum,
126-
deref(vect_ptr).data())
127-
StdVectorSentinel sentinel
128-
129-
if vector_DITYPE_t is vector[DTYPE_t]:
130-
sentinel = StdVectorSentinelDTYPE.create_for(vect_ptr)
131-
else:
132-
sentinel = StdVectorSentinelITYPE.create_for(vect_ptr)
133-
134-
# Makes the numpy array responsible of the life-cycle of its buffer.
135-
# A reference to the StdVectorSentinel will be stolen by the call to
136-
# `PyArray_SetBaseObject` below, so we increase its reference counter.
137-
# See: https://docs.python.org/3/c-api/intro.html#reference-count-details
138-
Py_INCREF(sentinel)
139-
np.PyArray_SetBaseObject(arr, sentinel)
140-
return arr
141-
142-
14379
cdef np.ndarray[object, ndim=1] coerce_vectors_to_nd_arrays(
14480
shared_ptr[vector_vector_DITYPE_t] vecs
14581
):

sklearn/utils/_vector_sentinel.pxd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
cimport numpy as np
2+
3+
from libcpp.vector cimport vector
4+
from ..utils._typedefs cimport ITYPE_t, DTYPE_t
5+
6+
ctypedef fused vector_typed:
7+
vector[DTYPE_t]
8+
vector[ITYPE_t]
9+
10+
cdef np.ndarray vector_to_nd_array(vector_typed * vect_ptr)

sklearn/utils/_vector_sentinel.pyx

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from cython.operator cimport dereference as deref
2+
from cpython.ref cimport Py_INCREF
3+
cimport numpy as np
4+
5+
from ._typedefs cimport DTYPECODE, ITYPECODE
6+
7+
np.import_array()
8+
9+
10+
cdef StdVectorSentinel _create_sentinel(vector_typed * vect_ptr):
11+
if vector_typed is vector[DTYPE_t]:
12+
return StdVectorSentinelFloat64.create_for(vect_ptr)
13+
else:
14+
return StdVectorSentinelIntP.create_for(vect_ptr)
15+
16+
17+
cdef class StdVectorSentinel:
18+
"""Wraps a reference to a vector which will be deallocated with this object.
19+
20+
When created, the StdVectorSentinel swaps the reference of its internal
21+
vectors with the provided one (vect_ptr), thus making the StdVectorSentinel
22+
manage the provided one's lifetime.
23+
"""
24+
cdef void* get_data(self):
25+
"""Return pointer to data."""
26+
27+
cdef int get_typenum(self):
28+
"""Get typenum for PyArray_SimpleNewFromData."""
29+
30+
31+
cdef class StdVectorSentinelFloat64(StdVectorSentinel):
32+
cdef vector[DTYPE_t] vec
33+
34+
@staticmethod
35+
cdef StdVectorSentinel create_for(vector[DTYPE_t] * vect_ptr):
36+
# This initializes the object directly without calling __init__
37+
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
38+
cdef StdVectorSentinelFloat64 sentinel = StdVectorSentinelFloat64.__new__(StdVectorSentinelFloat64)
39+
sentinel.vec.swap(deref(vect_ptr))
40+
return sentinel
41+
42+
cdef void* get_data(self):
43+
return self.vec.data()
44+
45+
cdef int get_typenum(self):
46+
return DTYPECODE
47+
48+
49+
cdef class StdVectorSentinelIntP(StdVectorSentinel):
50+
cdef vector[ITYPE_t] vec
51+
52+
@staticmethod
53+
cdef StdVectorSentinel create_for(vector[ITYPE_t] * vect_ptr):
54+
# This initializes the object directly without calling __init__
55+
# See: https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers # noqa
56+
cdef StdVectorSentinelIntP sentinel = StdVectorSentinelIntP.__new__(StdVectorSentinelIntP)
57+
sentinel.vec.swap(deref(vect_ptr))
58+
return sentinel
59+
60+
cdef void* get_data(self):
61+
return self.vec.data()
62+
63+
cdef int get_typenum(self):
64+
return ITYPECODE
65+
66+
67+
cdef np.ndarray vector_to_nd_array(vector_typed * vect_ptr):
68+
cdef:
69+
np.npy_intp size = deref(vect_ptr).size()
70+
StdVectorSentinel sentinel = _create_sentinel(vect_ptr)
71+
np.ndarray arr = np.PyArray_SimpleNewFromData(
72+
1, &size, sentinel.get_typenum(), sentinel.get_data())
73+
74+
# Makes the numpy array responsible of the life-cycle of its buffer.
75+
# A reference to the StdVectorSentinel will be stolen by the call to
76+
# `PyArray_SetBaseObject` below, so we increase its reference counter.
77+
# See: https://docs.python.org/3/c-api/intro.html#reference-count-details
78+
Py_INCREF(sentinel)
79+
np.PyArray_SetBaseObject(arr, sentinel)
80+
return arr

sklearn/utils/setup.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ def configuration(parent_package="", top_path=None):
109109
libraries=libraries,
110110
)
111111

112+
config.add_extension(
113+
"_vector_sentinel",
114+
sources=["_vector_sentinel.pyx"],
115+
include_dirs=[numpy.get_include()],
116+
libraries=libraries,
117+
language="c++",
118+
)
119+
112120
config.add_subpackage("tests")
113121

114122
return config

0 commit comments

Comments
 (0)