Generated by Cython 0.29.23
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
Raw output: distance_matrix_reachable.c
+001: # -*- coding: utf-8 -*-
__pyx_t_2 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_2) < 0) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
002:
003:
004: # pept is a Python library that unifies Positron Emission Particle
005: # Tracking (PEPT) research, including tracking, simulation, data analysis
006: # and visualisation tools.
007: #
008: # If you used this codebase or any software making use of it in a scientific
009: # publication, you must cite the following paper:
010: # Nicuşan AL, Windows-Yule CR. Positron emission particle tracking
011: # using machine learning. Review of Scientific Instruments.
012: # 2020 Jan 1;91(1):013329.
013: # https://doi.org/10.1063/1.5129251
014: #
015: # Copyright (C) 2019-2021 the pept developers
016: #
017: # This program is free software: you can redistribute it and/or modify
018: # it under the terms of the GNU General Public License as published by
019: # the Free Software Foundation, either version 3 of the License, or
020: # (at your option) any later version.
021: #
022: # This program is distributed in the hope that it will be useful,
023: # but WITHOUT ANY WARRANTY; without even the implied warranty of
024: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025: # GNU General Public License for more details.
026: #
027: # You should have received a copy of the GNU General Public License
028: # along with this program. If not, see <https://www.gnu.org/licenses/>.
029: # pept is a Python library that unifies Positron Emission Particle
030: # Tracking (PEPT) research, including tracking, simulation, data analysis
031: # and visualisation tools
032:
033:
034: # File : distance_matrix_reachable.pyx
035: # License : GNU v3.0
036: # Author : Andrei Leonard Nicusan <a.l.nicusan@bham.ac.uk>
037: # Date : 10.06.2020
038:
039:
040: # cython: language_level=3
041: # cython: boundscheck=False
042: # cython: wraparound=False
043: # cython: initializedcheck=False
044: # cython: nonecheck=False
045: # cython: embedsignature=True
046: # cython: cdivision=True
047:
048:
+049: import numpy as np
__pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 49, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) __PYX_ERR(0, 49, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+050: from scipy.sparse import csr_matrix
__pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 50, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __Pyx_INCREF(__pyx_n_s_csr_matrix); __Pyx_GIVEREF(__pyx_n_s_csr_matrix); PyList_SET_ITEM(__pyx_t_1, 0, __pyx_n_s_csr_matrix); __pyx_t_2 = __Pyx_Import(__pyx_n_s_scipy_sparse, __pyx_t_1, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 50, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_csr_matrix); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 50, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_csr_matrix, __pyx_t_1) < 0) __PYX_ERR(0, 50, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
051:
052: from libc.float cimport DBL_MIN
053: from libc.math cimport sqrt
054: cimport numpy as np
055:
056:
+057: cpdef distance_matrix_reachable(
static PyObject *__pyx_pw_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_1distance_matrix_reachable(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static PyObject *__pyx_f_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_distance_matrix_reachable(__Pyx_memviewslice __pyx_v_pts, int __pyx_v_points_window, CYTHON_UNUSED int __pyx_skip_dispatch) { Py_ssize_t __pyx_v_n; Py_ssize_t __pyx_v_p; Py_ssize_t __pyx_v_ndists; PyArrayObject *__pyx_v_dists_arr = 0; PyArrayObject *__pyx_v_rows_arr = 0; PyArrayObject *__pyx_v_cols_arr = 0; __Pyx_memviewslice __pyx_v_dists = { 0, 0, { 0 }, { 0 }, { 0 } }; __Pyx_memviewslice __pyx_v_rows = { 0, 0, { 0 }, { 0 }, { 0 } }; __Pyx_memviewslice __pyx_v_cols = { 0, 0, { 0 }, { 0 }, { 0 } }; Py_ssize_t __pyx_v_ie; Py_ssize_t __pyx_v_i; Py_ssize_t __pyx_v_j; double __pyx_v_dist; PyObject *__pyx_v_distance_matrix = NULL; __Pyx_LocalBuf_ND __pyx_pybuffernd_cols_arr; __Pyx_Buffer __pyx_pybuffer_cols_arr; __Pyx_LocalBuf_ND __pyx_pybuffernd_dists_arr; __Pyx_Buffer __pyx_pybuffer_dists_arr; __Pyx_LocalBuf_ND __pyx_pybuffernd_rows_arr; __Pyx_Buffer __pyx_pybuffer_rows_arr; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("distance_matrix_reachable", 0); __pyx_pybuffer_dists_arr.pybuffer.buf = NULL; __pyx_pybuffer_dists_arr.refcount = 0; __pyx_pybuffernd_dists_arr.data = NULL; __pyx_pybuffernd_dists_arr.rcbuffer = &__pyx_pybuffer_dists_arr; __pyx_pybuffer_rows_arr.pybuffer.buf = NULL; __pyx_pybuffer_rows_arr.refcount = 0; __pyx_pybuffernd_rows_arr.data = NULL; __pyx_pybuffernd_rows_arr.rcbuffer = &__pyx_pybuffer_rows_arr; __pyx_pybuffer_cols_arr.pybuffer.buf = NULL; __pyx_pybuffer_cols_arr.refcount = 0; __pyx_pybuffernd_cols_arr.data = NULL; __pyx_pybuffernd_cols_arr.rcbuffer = &__pyx_pybuffer_cols_arr; /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_5); __Pyx_XDECREF(__pyx_t_6); __Pyx_XDECREF(__pyx_t_7); __Pyx_XDECREF(__pyx_t_8); __PYX_XDEC_MEMVIEW(&__pyx_t_12, 1); __Pyx_XDECREF(__pyx_t_30); { PyObject *__pyx_type, *__pyx_value, *__pyx_tb; __Pyx_PyThreadState_declare __Pyx_PyThreadState_assign __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_cols_arr.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_dists_arr.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_rows_arr.rcbuffer->pybuffer); __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);} __Pyx_AddTraceback("pept.tracking.trajectory_separation.distance_matrix_reachable.distance_matrix_reachable", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = 0; goto __pyx_L2; __pyx_L0:; __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_cols_arr.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_dists_arr.rcbuffer->pybuffer); __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_rows_arr.rcbuffer->pybuffer); __pyx_L2:; __Pyx_XDECREF((PyObject *)__pyx_v_dists_arr); __Pyx_XDECREF((PyObject *)__pyx_v_rows_arr); __Pyx_XDECREF((PyObject *)__pyx_v_cols_arr); __PYX_XDEC_MEMVIEW(&__pyx_v_dists, 1); __PYX_XDEC_MEMVIEW(&__pyx_v_rows, 1); __PYX_XDEC_MEMVIEW(&__pyx_v_cols, 1); __Pyx_XDECREF(__pyx_v_distance_matrix); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_1distance_matrix_reachable(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_distance_matrix_reachable[] = "distance_matrix_reachable(double[:, :] pts, int points_window)\nCompute the distance matrix from a time-sorted array of points `pts`\n based on a sliding `points_window`.\n\n ::\n\n Function signature:\n distance_matrix_reachable(\n double[:, :] pts, # Array of points, cols = [t, x, y, z, ...]\n int points_window\n )\n\n The distance between the points (pts[i], pts[j]) is stored in the distance\n matrix at indices (i, j), making it upper-triangular.\n\n The distance matrix is created and returned using SciPy's sparse CSR\n matrix format. This saves a lot in terms of memory usage, especially for\n time-series data such as moving tracers, as not all points can be\n connected. This format is also closer to the mathematical formulation of\n \"reachable points\" in terms of undirected (incomplete) graphs - namely\n storing edges as a list of pairs of vertices.\n\n This is a low-level Cython function that does not do any checks on the\n input data - it is meant to be used in other modules / libraries; in\n particular, the `pept.tracking.trajectory_separation` module.\n\n Parameters\n ----------\n pts : (M, N>=4) numpy.ndarray\n The points from multiple trajectories. Each row in `pts` will\n have a timestamp and the 3 spatial coordinates, such that the data\n columns are [time, x_coord, y_coord, z_coord]. Note that `point_data`\n can have more data columns and they will simply be ignored.\n points_window : int\n Two points are \"reachable\" (i.e. they can be connected) if and only if\n they are within `points_window` in the time-sorted input `pts`. As the\n points from different trajectories are intertwined (e.g. for two\n tracers A and B, the `pts` array might have two entries for A,\n followed by three entries for B, then one entry for A, etc.), this\n should optimally be the largest number o""f points in the input array\n between two consecutive points on the same trajectory. If\n `pts` is too small, all points in the dataset will be unreachable.\n Naturally, a larger `time_window` correponds to more pairs needing to\n be checked (and the function will take a longer to complete).\n\n Returns\n -------\n distance_matrix : CSR <NxN sparse matrix of type '<class 'numpy.float64'>\n A SciPy sparse matrix in the CSR format, containing the distances\n between every pair of reachable points in `pts`.\n\n Notes\n -----\n In order for the `points_window` to act as a sliding window, in effect only\n connecting points which are around the same timeframe, the points should be\n sorted based on thetime column (the first row) in `pts`. This should be\n done *prior* to calling this function.\n "; static PyObject *__pyx_pw_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_1distance_matrix_reachable(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { __Pyx_memviewslice __pyx_v_pts = { 0, 0, { 0 }, { 0 }, { 0 } }; int __pyx_v_points_window; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("distance_matrix_reachable (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_pts,&__pyx_n_s_points_window,0}; PyObject* values[2] = {0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_pts)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_points_window)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("distance_matrix_reachable", 1, 2, 2, 1); __PYX_ERR(0, 57, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "distance_matrix_reachable") < 0)) __PYX_ERR(0, 57, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 2) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); } __pyx_v_pts = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(values[0], PyBUF_WRITABLE); if (unlikely(!__pyx_v_pts.memview)) __PYX_ERR(0, 58, __pyx_L3_error) __pyx_v_points_window = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_points_window == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 59, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("distance_matrix_reachable", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 57, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("pept.tracking.trajectory_separation.distance_matrix_reachable.distance_matrix_reachable", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_distance_matrix_reachable(__pyx_self, __pyx_v_pts, __pyx_v_points_window); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_distance_matrix_reachable(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_pts, int __pyx_v_points_window) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("distance_matrix_reachable", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = __pyx_f_4pept_8tracking_21trajectory_separation_25distance_matrix_reachable_distance_matrix_reachable(__pyx_v_pts, __pyx_v_points_window, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 57, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("pept.tracking.trajectory_separation.distance_matrix_reachable.distance_matrix_reachable", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __PYX_XDEC_MEMVIEW(&__pyx_v_pts, 1); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
058: double[:, :] pts, # Array of points, cols = [t, x, y, z, ...]
059: int points_window
060: ):
061: '''Compute the distance matrix from a time-sorted array of points `pts`
062: based on a sliding `points_window`.
063:
064: ::
065:
066: Function signature:
067: distance_matrix_reachable(
068: double[:, :] pts, # Array of points, cols = [t, x, y, z, ...]
069: int points_window
070: )
071:
072: The distance between the points (pts[i], pts[j]) is stored in the distance
073: matrix at indices (i, j), making it upper-triangular.
074:
075: The distance matrix is created and returned using SciPy's sparse CSR
076: matrix format. This saves a lot in terms of memory usage, especially for
077: time-series data such as moving tracers, as not all points can be
078: connected. This format is also closer to the mathematical formulation of
079: "reachable points" in terms of undirected (incomplete) graphs - namely
080: storing edges as a list of pairs of vertices.
081:
082: This is a low-level Cython function that does not do any checks on the
083: input data - it is meant to be used in other modules / libraries; in
084: particular, the `pept.tracking.trajectory_separation` module.
085:
086: Parameters
087: ----------
088: pts : (M, N>=4) numpy.ndarray
089: The points from multiple trajectories. Each row in `pts` will
090: have a timestamp and the 3 spatial coordinates, such that the data
091: columns are [time, x_coord, y_coord, z_coord]. Note that `point_data`
092: can have more data columns and they will simply be ignored.
093: points_window : int
094: Two points are "reachable" (i.e. they can be connected) if and only if
095: they are within `points_window` in the time-sorted input `pts`. As the
096: points from different trajectories are intertwined (e.g. for two
097: tracers A and B, the `pts` array might have two entries for A,
098: followed by three entries for B, then one entry for A, etc.), this
099: should optimally be the largest number of points in the input array
100: between two consecutive points on the same trajectory. If
101: `pts` is too small, all points in the dataset will be unreachable.
102: Naturally, a larger `time_window` correponds to more pairs needing to
103: be checked (and the function will take a longer to complete).
104:
105: Returns
106: -------
107: distance_matrix : CSR <NxN sparse matrix of type '<class 'numpy.float64'>
108: A SciPy sparse matrix in the CSR format, containing the distances
109: between every pair of reachable points in `pts`.
110:
111: Notes
112: -----
113: In order for the `points_window` to act as a sliding window, in effect only
114: connecting points which are around the same timeframe, the points should be
115: sorted based on thetime column (the first row) in `pts`. This should be
116: done *prior* to calling this function.
117: '''
118:
119: # Use Py_ssize_t as we will access C arrays (memoryviews on numpy arrays).
120: # That is the "proper" type of a C array pointer / index.
+121: cdef Py_ssize_t n = pts.shape[0] # Total number of points
__pyx_v_n = (__pyx_v_pts.shape[0]);
+122: cdef Py_ssize_t p = min(n, points_window)
__pyx_t_1 = __pyx_v_points_window; __pyx_t_2 = __pyx_v_n; if (((__pyx_t_1 < __pyx_t_2) != 0)) { __pyx_t_3 = __pyx_t_1; } else { __pyx_t_3 = __pyx_t_2; } __pyx_v_p = __pyx_t_3;
123:
124: # Calculate sparse distance matrix between reachable points. The number of
125: # points we need to check `ndists` is given by the formula below.
+126: cdef Py_ssize_t ndists = (p + 1) * (n - p) + p * (p + 1) // 2 - n
__pyx_v_ndists = ((((__pyx_v_p + 1) * (__pyx_v_n - __pyx_v_p)) + ((__pyx_v_p * (__pyx_v_p + 1)) / 2)) - __pyx_v_n);
127:
128: # Pre-allocate the arrays for creating the sparse distance matrix. In the
129: # sparse matrix, every data point `dists` has an associated row in `rows`
130: # column in `cols`.
+131: cdef np.ndarray[double, ndim = 1] dists_arr = np.zeros(ndists, dtype =
__Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_n_s_np); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_zeros); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyInt_FromSsize_t(__pyx_v_ndists); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_6 = PyTuple_New(1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_6); __Pyx_GIVEREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); /* … */ __pyx_t_8 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_6, __pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; if (!(likely(((__pyx_t_8) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_8, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 131, __pyx_L1_error) __pyx_t_9 = ((PyArrayObject *)__pyx_t_8); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_dists_arr.rcbuffer->pybuffer, (PyObject*)__pyx_t_9, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { __pyx_v_dists_arr = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_dists_arr.rcbuffer->pybuffer.buf = NULL; __PYX_ERR(0, 131, __pyx_L1_error) } else {__pyx_pybuffernd_dists_arr.diminfo[0].strides = __pyx_pybuffernd_dists_arr.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_dists_arr.diminfo[0].shape = __pyx_pybuffernd_dists_arr.rcbuffer->pybuffer.shape[0]; } } __pyx_t_9 = 0; __pyx_v_dists_arr = ((PyArrayObject *)__pyx_t_8); __pyx_t_8 = 0;
+132: np.float64)
__Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_n_s_np); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 132, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_7, __pyx_n_s_float64); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 132, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_dtype, __pyx_t_8) < 0) __PYX_ERR(0, 131, __pyx_L1_error) __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+133: cdef np.ndarray[double, ndim = 1] rows_arr = np.zeros(ndists, dtype =
__Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_n_s_np); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_8, __pyx_n_s_zeros); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; __pyx_t_8 = PyInt_FromSsize_t(__pyx_v_ndists); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __pyx_t_6 = PyTuple_New(1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_6); __Pyx_GIVEREF(__pyx_t_8); PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_8); __pyx_t_8 = 0; __pyx_t_8 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); /* … */ __pyx_t_7 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_6, __pyx_t_8); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; if (!(likely(((__pyx_t_7) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_7, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 133, __pyx_L1_error) __pyx_t_10 = ((PyArrayObject *)__pyx_t_7); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_rows_arr.rcbuffer->pybuffer, (PyObject*)__pyx_t_10, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { __pyx_v_rows_arr = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_rows_arr.rcbuffer->pybuffer.buf = NULL; __PYX_ERR(0, 133, __pyx_L1_error) } else {__pyx_pybuffernd_rows_arr.diminfo[0].strides = __pyx_pybuffernd_rows_arr.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_rows_arr.diminfo[0].shape = __pyx_pybuffernd_rows_arr.rcbuffer->pybuffer.shape[0]; } } __pyx_t_10 = 0; __pyx_v_rows_arr = ((PyArrayObject *)__pyx_t_7); __pyx_t_7 = 0;
+134: np.float64)
__Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_n_s_np); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 134, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __pyx_t_7 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_float64); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 134, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; if (PyDict_SetItem(__pyx_t_8, __pyx_n_s_dtype, __pyx_t_7) < 0) __PYX_ERR(0, 133, __pyx_L1_error) __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+135: cdef np.ndarray[double, ndim = 1] cols_arr = np.zeros(ndists, dtype =
__Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_n_s_np); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_7, __pyx_n_s_zeros); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; __pyx_t_7 = PyInt_FromSsize_t(__pyx_v_ndists); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __pyx_t_6 = PyTuple_New(1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_6); __Pyx_GIVEREF(__pyx_t_7); PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_7); __pyx_t_7 = 0; __pyx_t_7 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); /* … */ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_8, __pyx_t_6, __pyx_t_7); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 135, __pyx_L1_error) __pyx_t_11 = ((PyArrayObject *)__pyx_t_5); { __Pyx_BufFmt_StackElem __pyx_stack[1]; if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_cols_arr.rcbuffer->pybuffer, (PyObject*)__pyx_t_11, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { __pyx_v_cols_arr = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_cols_arr.rcbuffer->pybuffer.buf = NULL; __PYX_ERR(0, 135, __pyx_L1_error) } else {__pyx_pybuffernd_cols_arr.diminfo[0].strides = __pyx_pybuffernd_cols_arr.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_cols_arr.diminfo[0].shape = __pyx_pybuffernd_cols_arr.rcbuffer->pybuffer.shape[0]; } } __pyx_t_11 = 0; __pyx_v_cols_arr = ((PyArrayObject *)__pyx_t_5); __pyx_t_5 = 0;
+136: np.float64)
__Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_n_s_np); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 136, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_float64); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 136, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; if (PyDict_SetItem(__pyx_t_7, __pyx_n_s_dtype, __pyx_t_5) < 0) __PYX_ERR(0, 135, __pyx_L1_error) __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
137:
138: # We'll work with memoryviews on the above arrays:
+139: cdef double[:] dists = dists_arr
__pyx_t_12 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(((PyObject *)__pyx_v_dists_arr), PyBUF_WRITABLE); if (unlikely(!__pyx_t_12.memview)) __PYX_ERR(0, 139, __pyx_L1_error) __pyx_v_dists = __pyx_t_12; __pyx_t_12.memview = NULL; __pyx_t_12.data = NULL;
+140: cdef double[:] rows = rows_arr
__pyx_t_12 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(((PyObject *)__pyx_v_rows_arr), PyBUF_WRITABLE); if (unlikely(!__pyx_t_12.memview)) __PYX_ERR(0, 140, __pyx_L1_error) __pyx_v_rows = __pyx_t_12; __pyx_t_12.memview = NULL; __pyx_t_12.data = NULL;
+141: cdef double[:] cols = cols_arr
__pyx_t_12 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(((PyObject *)__pyx_v_cols_arr), PyBUF_WRITABLE); if (unlikely(!__pyx_t_12.memview)) __PYX_ERR(0, 141, __pyx_L1_error) __pyx_v_cols = __pyx_t_12; __pyx_t_12.memview = NULL; __pyx_t_12.data = NULL;
142:
143: # Calculate the distances between reachable points.
+144: cdef Py_ssize_t ie = 0 # distance index
__pyx_v_ie = 0;
145: cdef Py_ssize_t i, j # iterators
146: cdef double dist # distance between two points
147:
+148: with nogil:
{ #ifdef WITH_THREAD PyThreadState *_save; Py_UNBLOCK_THREADS __Pyx_FastGIL_Remember(); #endif /*try:*/ { /* … */ /*finally:*/ { /*normal exit:*/{ #ifdef WITH_THREAD __Pyx_FastGIL_Forget(); Py_BLOCK_THREADS #endif goto __pyx_L5; } __pyx_L5:; } }
+149: for i in range(n - 1):
__pyx_t_3 = (__pyx_v_n - 1); __pyx_t_2 = __pyx_t_3; for (__pyx_t_13 = 0; __pyx_t_13 < __pyx_t_2; __pyx_t_13+=1) { __pyx_v_i = __pyx_t_13;
+150: for j in range(i + 1, min(i + p, n - 1) + 1):
__pyx_t_14 = (__pyx_v_n - 1); __pyx_t_15 = (__pyx_v_i + __pyx_v_p); if (((__pyx_t_14 < __pyx_t_15) != 0)) { __pyx_t_16 = __pyx_t_14; } else { __pyx_t_16 = __pyx_t_15; } __pyx_t_14 = (__pyx_t_16 + 1); __pyx_t_16 = __pyx_t_14; for (__pyx_t_15 = (__pyx_v_i + 1); __pyx_t_15 < __pyx_t_16; __pyx_t_15+=1) { __pyx_v_j = __pyx_t_15;
151: # Euclidean distance between points i, j in `pts`
152: # dist = np.linalg.norm(pts[i, 1:4] - pts[j, 1:4])
+153: dist = sqrt(
__pyx_v_dist = sqrt(((pow(((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pts.data + __pyx_t_17 * __pyx_v_pts.strides[0]) ) + __pyx_t_18 * __pyx_v_pts.strides[1]) ))) - (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pts.data + __pyx_t_19 * __pyx_v_pts.strides[0]) ) + __pyx_t_20 * __pyx_v_pts.strides[1]) )))), 2.0) + pow(((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pts.data + __pyx_t_21 * __pyx_v_pts.strides[0]) ) + __pyx_t_22 * __pyx_v_pts.strides[1]) ))) - (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pts.data + __pyx_t_23 * __pyx_v_pts.strides[0]) ) + __pyx_t_24 * __pyx_v_pts.strides[1]) )))), 2.0)) + pow(((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pts.data + __pyx_t_25 * __pyx_v_pts.strides[0]) ) + __pyx_t_26 * __pyx_v_pts.strides[1]) ))) - (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pts.data + __pyx_t_27 * __pyx_v_pts.strides[0]) ) + __pyx_t_28 * __pyx_v_pts.strides[1]) )))), 2.0)));
+154: (pts[j, 1] - pts[i, 1]) ** 2 +
__pyx_t_17 = __pyx_v_j; __pyx_t_18 = 1; __pyx_t_19 = __pyx_v_i; __pyx_t_20 = 1;
+155: (pts[j, 2] - pts[i, 2]) ** 2 +
__pyx_t_21 = __pyx_v_j; __pyx_t_22 = 2; __pyx_t_23 = __pyx_v_i; __pyx_t_24 = 2;
+156: (pts[j, 3] - pts[i, 3]) ** 2
__pyx_t_25 = __pyx_v_j; __pyx_t_26 = 3; __pyx_t_27 = __pyx_v_i; __pyx_t_28 = 3;
157: )
158:
159: # Fix bug (or feature?) of scipy's minimum_spanning_tree where
160: # duplicate points (i.e. dist == 0.0) are ommitted from the
161: # MST vertices.
+162: dists[ie] = dist if dist != 0.0 else DBL_MIN
if (((__pyx_v_dist != 0.0) != 0)) { __pyx_t_29 = __pyx_v_dist; } else { __pyx_t_29 = DBL_MIN; } __pyx_t_28 = __pyx_v_ie; *((double *) ( /* dim=0 */ (__pyx_v_dists.data + __pyx_t_28 * __pyx_v_dists.strides[0]) )) = __pyx_t_29;
+163: rows[ie] = i
__pyx_t_28 = __pyx_v_ie; *((double *) ( /* dim=0 */ (__pyx_v_rows.data + __pyx_t_28 * __pyx_v_rows.strides[0]) )) = __pyx_v_i;
+164: cols[ie] = j
__pyx_t_28 = __pyx_v_ie; *((double *) ( /* dim=0 */ (__pyx_v_cols.data + __pyx_t_28 * __pyx_v_cols.strides[0]) )) = __pyx_v_j;
+165: ie = ie + 1
__pyx_v_ie = (__pyx_v_ie + 1); } } }
166:
167: # Create the distance matrix from the found points.
+168: distance_matrix = csr_matrix(
__Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_n_s_csr_matrix); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 168, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); /* … */ __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 168, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_GIVEREF(__pyx_t_8); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_8); __pyx_t_8 = 0; /* … */ __pyx_t_30 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_4, __pyx_t_8); if (unlikely(!__pyx_t_30)) __PYX_ERR(0, 168, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_30); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; __pyx_v_distance_matrix = __pyx_t_30; __pyx_t_30 = 0;
+169: (dists, (rows, cols)),
__pyx_t_7 = __pyx_memoryview_fromslice(__pyx_v_dists, 1, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 169, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __pyx_t_6 = __pyx_memoryview_fromslice(__pyx_v_rows, 1, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 169, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_6); __pyx_t_8 = __pyx_memoryview_fromslice(__pyx_v_cols, 1, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 169, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 169, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_GIVEREF(__pyx_t_6); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_6); __Pyx_GIVEREF(__pyx_t_8); PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_t_8); __pyx_t_6 = 0; __pyx_t_8 = 0; __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 169, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __Pyx_GIVEREF(__pyx_t_7); PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_7); __Pyx_GIVEREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_4); __pyx_t_7 = 0; __pyx_t_4 = 0;
+170: shape = (n, n)
__pyx_t_8 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 170, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_8); __pyx_t_7 = PyInt_FromSsize_t(__pyx_v_n); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 170, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __pyx_t_6 = PyInt_FromSsize_t(__pyx_v_n); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 170, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_6); __pyx_t_30 = PyTuple_New(2); if (unlikely(!__pyx_t_30)) __PYX_ERR(0, 170, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_30); __Pyx_GIVEREF(__pyx_t_7); PyTuple_SET_ITEM(__pyx_t_30, 0, __pyx_t_7); __Pyx_GIVEREF(__pyx_t_6); PyTuple_SET_ITEM(__pyx_t_30, 1, __pyx_t_6); __pyx_t_7 = 0; __pyx_t_6 = 0; if (PyDict_SetItem(__pyx_t_8, __pyx_n_s_shape, __pyx_t_30) < 0) __PYX_ERR(0, 170, __pyx_L1_error) __Pyx_DECREF(__pyx_t_30); __pyx_t_30 = 0;
171: )
172:
+173: return distance_matrix
__Pyx_XDECREF(__pyx_r); __Pyx_INCREF(__pyx_v_distance_matrix); __pyx_r = __pyx_v_distance_matrix; goto __pyx_L0;