77static bool scheduler_initialized = false ;
88static PyObject* scheduler_cleanup_weakref = nullptr ;
99
10- static void cleanup_scheduler (PyObject *capsule)
10+ static void cleanup_scheduler (PyObject *capsule= nullptr )
1111{
12- if (scheduler_initialized)
13- {
14- parlay::internal::stop_scheduler ();
15- scheduler_initialized = false ;
16- }
12+ if (scheduler_initialized)
13+ {
14+ parlay::internal::stop_scheduler ();
15+ scheduler_initialized = false ;
16+ }
1717}
1818
1919static void ensure_scheduler_initialized ()
@@ -40,7 +40,7 @@ static PyObject* DBSCAN_py(PyObject* self, PyObject* args, PyObject *kwargs)
4040 return NULL ;
4141 }
4242
43- // Check the number of dimensions and that we actually recieved an np.ndarray
43+ // Check the number of dimensions and that we actually received an np.ndarray
4444 X = (PyArrayObject*)PyArray_FROMANY (
4545 Xobj,
4646 NPY_DOUBLE,
@@ -79,7 +79,10 @@ static PyObject* DBSCAN_py(PyObject* self, PyObject* args, PyObject *kwargs)
7979 PyArrayObject* core_samples = (PyArrayObject*)PyArray_SimpleNew (1 , &n, NPY_BOOL);
8080 PyArrayObject* labels = (PyArrayObject*)PyArray_SimpleNew (1 , &n, NPY_INT);
8181
82- ensure_scheduler_initialized ();
82+ if (!parlay::sequential)
83+ {
84+ ensure_scheduler_initialized ();
85+ }
8386
8487 DBSCAN (
8588 dim,
@@ -98,9 +101,27 @@ static PyObject* DBSCAN_py(PyObject* self, PyObject* args, PyObject *kwargs)
98101 return result_tuple;
99102}
100103
104+ static PyObject* set_sequential_py (PyObject* self, PyObject* args)
105+ {
106+ int state = 1 ;
107+ if (!PyArg_ParseTuple (args, " |p" , &state)) {
108+ return nullptr ;
109+ }
110+ parlay::sequential = state == 1 ;
111+ if (parlay::sequential) {
112+ cleanup_scheduler ();
113+ }
114+ Py_RETURN_NONE;
115+ }
116+
117+ static PyObject* get_sequential_py (PyObject* self, PyObject* args)
118+ {
119+ return PyBool_FromLong (parlay::sequential ? 1 : 0 );
120+ }
121+
101122PyDoc_STRVAR (doc_DBSCAN,
102123" DBSCAN(X, eps=0.5, min_samples=5)\n --\n\n \
103- Run DBSCAN on a set of n samples of dimension dim with a minimum seperation \n \
124+ Run DBSCAN on a set of n samples of dimension dim with a minimum separation \n \
104125between the clusters (which must include at least min_samples) of eps. Points\n \
105126that do not fit in any cluster are labeled as noise (-1).\n \
106127\n \
@@ -113,7 +134,7 @@ Parameters\n\
113134X : np.ndarray[tuple[n, dim], np.float64]\n \
114135 2-D array representing the samples.\n \
115136eps : float\n \
116- minimum seperation between the clusters.\n \
137+ minimum separation between the clusters.\n \
117138min_samples : int\n \
118139 minimum number of samples in the clusters.\n \
119140\n \
@@ -125,8 +146,31 @@ core_samples : np.ndarray[tuple[n], np.bool_]\n\
125146 is each sample the core sample of its cluster\n \
126147\n " );
127148
149+ PyDoc_STRVAR (doc_set_sequential,
150+ " set_sequential(state=True)\n --\n\n \
151+ Set whether DBSCAN runs in sequential mode (single-threaded).\n \
152+ This mode is potentially more efficient.\n \
153+ \n \
154+ Parameters\n \
155+ ----------\n \
156+ state : bool, default True\n \
157+ If True, run sequentially. If False, allow parallel execution.\n \
158+ " );
159+
160+ PyDoc_STRVAR (doc_get_sequential,
161+ " get_sequential()\n --\n\n \
162+ Return the current state of the sequential setting.\n \
163+ \n \
164+ Returns\n \
165+ -------\n \
166+ state : bool\n \
167+ True if running sequentially, False if in parallel mode.\n \
168+ " );
169+
128170static struct PyMethodDef methods[] = {
129171 {" DBSCAN" , (PyCFunction)(void *)(PyCFunctionWithKeywords) DBSCAN_py, METH_VARARGS | METH_KEYWORDS, doc_DBSCAN},
172+ {" set_sequential" , (PyCFunction)set_sequential_py, METH_VARARGS, doc_set_sequential},
173+ {" get_sequential" , (PyCFunction)get_sequential_py, METH_NOARGS, doc_get_sequential},
130174 {NULL , NULL , 0 , NULL }
131175};
132176
0 commit comments