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: birmingham_method.c

+001: #!/usr/bin/env python3
  __pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 002: # -*- coding: utf-8 -*-
 003: 
 004: 
 005: #    pept is a Python library that unifies Positron Emission Particle
 006: #    Tracking (PEPT) research, including tracking, simulation, data analysis
 007: #    and visualisation tools.
 008: #
 009: #    If you used this codebase or any software making use of it in a scientific
 010: #    publication, you must cite the following paper:
 011: #        Nicuşan AL, Windows-Yule CR. Positron emission particle tracking
 012: #        using machine learning. Review of Scientific Instruments.
 013: #        2020 Jan 1;91(1):013329.
 014: #        https://doi.org/10.1063/1.5129251
 015: #
 016: #    Copyright (C) 2019-2021 the pept developers
 017: #
 018: #    This program is free software: you can redistribute it and/or modify
 019: #    it under the terms of the GNU General Public License as published by
 020: #    the Free Software Foundation, either version 3 of the License, or
 021: #    (at your option) any later version.
 022: #
 023: #    This program is distributed in the hope that it will be useful,
 024: #    but WITHOUT ANY WARRANTY; without even the implied warranty of
 025: #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 026: #    GNU General Public License for more details.
 027: #
 028: #    You should have received a copy of the GNU General Public License
 029: #    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 030: #    pept is a Python library that unifies Positron Emission Particle
 031: #    Tracking (PEPT) research, including tracking, simulation, data analysis
 032: #    and visualisation tools
 033: 
 034: 
 035: # File      : birmingham_method.pyx
 036: # License   : GNU v3.0
 037: # Author    : Sam Manger
 038: # Date      : 21.08.2019
 039: 
 040: 
 041: # cython: language_level=3
 042: # cython: boundscheck=False
 043: # cython: wraparound=False
 044: # cython: initializedcheck=False
 045: # cython: nonecheck=False
 046: # cython: embedsignature=True
 047: # cython: cdivision=True
 048: 
 049: 
+050: import numpy as np      # import numpy for Python functions
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, 0); 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_np, __pyx_t_1) < 0) __PYX_ERR(0, 50, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 051: cimport numpy as np     # import numpy for C functions (numpy's C API)
 052: 
 053: 
 054: cdef extern from "birmingham_method_ext.c":
 055:     # C is included here so that it doesn't need to be compiled externally
 056:     pass
 057: 
 058: 
 059: cdef extern from "birmingham_method_ext.h":
 060:     void birmingham_method_ext(
 061:         const double *, const Py_ssize_t, const Py_ssize_t,
 062:         double *, int *, const double
 063:     ) nogil
 064: 
 065:     void calculate(
 066:         double *, double *, double *, double *, double *, double *,
 067:         double *, double *, double *, double *, double *, double *,
 068:         double *, double *, double *, double *, double *,
 069:         int *, int, int, double *
 070:     ) nogil
 071: 
 072: 
 073: # cpdef means it is defined both for Python and C code;
 074: # cdef means it is defined only for C code;
 075: 
 076: # Cython has a cool function to automatically get memoryviews of the input
 077: # parameters => double[:, :] receives a 2D numpy array.
+078: cpdef birmingham_method(
static PyObject *__pyx_pw_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_1birmingham_method(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_birmingham_method(__Pyx_memviewslice __pyx_v_lines, double __pyx_v_fopt, CYTHON_UNUSED int __pyx_skip_dispatch) {
  Py_ssize_t __pyx_v_nrows;
  Py_ssize_t __pyx_v_ncols;
  PyArrayObject *__pyx_v_location = 0;
  PyArrayObject *__pyx_v_used = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_location;
  __Pyx_Buffer __pyx_pybuffer_location;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_used;
  __Pyx_Buffer __pyx_pybuffer_used;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("birmingham_method", 0);
  __pyx_pybuffer_location.pybuffer.buf = NULL;
  __pyx_pybuffer_location.refcount = 0;
  __pyx_pybuffernd_location.data = NULL;
  __pyx_pybuffernd_location.rcbuffer = &__pyx_pybuffer_location;
  __pyx_pybuffer_used.pybuffer.buf = NULL;
  __pyx_pybuffer_used.refcount = 0;
  __pyx_pybuffernd_used.data = NULL;
  __pyx_pybuffernd_used.rcbuffer = &__pyx_pybuffer_used;
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_6);
  { 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_location.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_used.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("pept.tracking.birmingham_method.extensions.birmingham_method.birmingham_method", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_location.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_used.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_location);
  __Pyx_XDECREF((PyObject *)__pyx_v_used);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static PyObject *__pyx_pw_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_1birmingham_method(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static char __pyx_doc_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_birmingham_method[] = "birmingham_method(double[:, :] lines, double fopt)\nUse the Birmingham Method to find one tracer location from the LoRs\n    stored in `lines`.\n\n    Function signature:\n        birmingham_method(\n            double[:, :] lines,     # LoRs in a sample\n            double fopt             # Fraction of LoRs used to find tracer\n        )\n\n    This function receives a numpy array of LoRs (one \"sample\") from python,\n    computing the minimum distance point (MDP). A number of lines that lie\n    outside the standard deviation of the MDP are then removed from the set,\n    and the MDP is recalculated. This process is repeated until approximately\n    a fixed fraction (fopt) of the original lines is left.\n\n    The found tracer position is then returned along with a boolean mask of\n    the LoRs that were used to compute it.\n\n    Parameters\n    ----------\n    lines : (N, M >= 7) numpy.ndarray\n        A numpy array of the lines of respones (LoRs) that will be used to find\n        a tracer location; each LoR is stored as a timestamp, the 3D\n        coordinates of two points defining the line, followed by any additional\n        data. The data columns are then `[time, x1, y1, z1, x2, y2, z2, etc]`.\n        Note that the extra data is simply ignored by this function.\n    fopt : float\n        A float number between 0 and 1 representing the fraction of LoRs that\n        will be used to compute the tracer location.\n\n    Returns\n    -------\n    location : (5,) numpy.ndarray\n        The computed tracer location, with data columns formatted as\n        `[time, x, y, z, error]`.\n    used : (N,) numpy.ndarray\n        A boolean mask of the LoRs that were used to compute the tracer\n        location; that is, a vector of the same length as `lines`, containing 1\n        for the rows that were used, and 0 otherwise.\n\n    Notes\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 ot""her modules / libraries. For a\n    normal user, the `pept.tracking.birmingham_method.BirminghamMethod` class\n    methods `fit_sample` and `fit` are recommended as higher-level APIs. They\n    do check the input parameters and are easier to use.\n    ";
static PyObject *__pyx_pw_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_1birmingham_method(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  __Pyx_memviewslice __pyx_v_lines = { 0, 0, { 0 }, { 0 }, { 0 } };
  double __pyx_v_fopt;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("birmingham_method (wrapper)", 0);
  {
    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_lines,&__pyx_n_s_fopt,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_lines)) != 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_fopt)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("birmingham_method", 1, 2, 2, 1); __PYX_ERR(0, 78, __pyx_L3_error)
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "birmingham_method") < 0)) __PYX_ERR(0, 78, __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_lines = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(values[0], PyBUF_WRITABLE); if (unlikely(!__pyx_v_lines.memview)) __PYX_ERR(0, 79, __pyx_L3_error)
    __pyx_v_fopt = __pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_fopt == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 80, __pyx_L3_error)
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid("birmingham_method", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 78, __pyx_L3_error)
  __pyx_L3_error:;
  __Pyx_AddTraceback("pept.tracking.birmingham_method.extensions.birmingham_method.birmingham_method", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_birmingham_method(__pyx_self, __pyx_v_lines, __pyx_v_fopt);
  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_17birmingham_method_10extensions_17birmingham_method_birmingham_method(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_lines, double __pyx_v_fopt) {
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("birmingham_method", 0);
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = __pyx_f_4pept_8tracking_17birmingham_method_10extensions_17birmingham_method_birmingham_method(__pyx_v_lines, __pyx_v_fopt, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 78, __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.birmingham_method.extensions.birmingham_method.birmingham_method", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __PYX_XDEC_MEMVIEW(&__pyx_v_lines, 1);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 079:     double[:, :] lines,
 080:     double fopt
 081: ):
 082:     '''Use the Birmingham Method to find one tracer location from the LoRs
 083:     stored in `lines`.
 084: 
 085:     Function signature:
 086:         birmingham_method(
 087:             double[:, :] lines,     # LoRs in a sample
 088:             double fopt             # Fraction of LoRs used to find tracer
 089:         )
 090: 
 091:     This function receives a numpy array of LoRs (one "sample") from python,
 092:     computing the minimum distance point (MDP). A number of lines that lie
 093:     outside the standard deviation of the MDP are then removed from the set,
 094:     and the MDP is recalculated. This process is repeated until approximately
 095:     a fixed fraction (fopt) of the original lines is left.
 096: 
 097:     The found tracer position is then returned along with a boolean mask of
 098:     the LoRs that were used to compute it.
 099: 
 100:     Parameters
 101:     ----------
 102:     lines : (N, M >= 7) numpy.ndarray
 103:         A numpy array of the lines of respones (LoRs) that will be used to find
 104:         a tracer location; each LoR is stored as a timestamp, the 3D
 105:         coordinates of two points defining the line, followed by any additional
 106:         data. The data columns are then `[time, x1, y1, z1, x2, y2, z2, etc]`.
 107:         Note that the extra data is simply ignored by this function.
 108:     fopt : float
 109:         A float number between 0 and 1 representing the fraction of LoRs that
 110:         will be used to compute the tracer location.
 111: 
 112:     Returns
 113:     -------
 114:     location : (5,) numpy.ndarray
 115:         The computed tracer location, with data columns formatted as
 116:         `[time, x, y, z, error]`.
 117:     used : (N,) numpy.ndarray
 118:         A boolean mask of the LoRs that were used to compute the tracer
 119:         location; that is, a vector of the same length as `lines`, containing 1
 120:         for the rows that were used, and 0 otherwise.
 121: 
 122:     Notes
 123:     -----
 124:     This is a low-level Cython function that does not do any checks on the
 125:     input data - it is meant to be used in other modules / libraries. For a
 126:     normal user, the `pept.tracking.birmingham_method.BirminghamMethod` class
 127:     methods `fit_sample` and `fit` are recommended as higher-level APIs. They
 128:     do check the input parameters and are easier to use.
 129:     '''
 130: 
 131:     # Py_ssize_t is the one "strange" type from Cython - it is the type used
 132:     # for indexing arrays, as pointers must have a certain number of bits that
 133:     # is platform-dependent; that type is stored as a C macro in `ssize_t` that
 134:     # Cython also provides as `Py_ssize_t`.
+135:     cdef Py_ssize_t nrows = lines.shape[0]
  __pyx_v_nrows = (__pyx_v_lines.shape[0]);
+136:     cdef Py_ssize_t ncols = lines.shape[1]
  __pyx_v_ncols = (__pyx_v_lines.shape[1]);
 137: 
 138:     # np.float64 == C double ; np.intc == C int ;
+139:     cdef np.ndarray[double, ndim = 1] location = np.zeros(5, dtype = np.float64)
  __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_n_s_np); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_zeros); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_np); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_float64); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (PyDict_SetItem(__pyx_t_1, __pyx_n_s_dtype, __pyx_t_4) < 0) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple_, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 139, __pyx_L1_error)
  __pyx_t_5 = ((PyArrayObject *)__pyx_t_4);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_location.rcbuffer->pybuffer, (PyObject*)__pyx_t_5, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_location = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_location.rcbuffer->pybuffer.buf = NULL;
      __PYX_ERR(0, 139, __pyx_L1_error)
    } else {__pyx_pybuffernd_location.diminfo[0].strides = __pyx_pybuffernd_location.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_location.diminfo[0].shape = __pyx_pybuffernd_location.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_5 = 0;
  __pyx_v_location = ((PyArrayObject *)__pyx_t_4);
  __pyx_t_4 = 0;
/* … */
  __pyx_tuple_ = PyTuple_Pack(1, __pyx_int_5); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 139, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
+140:     cdef np.ndarray[int, ndim = 1] used = np.ones(nrows, dtype = np.intc)
  __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_n_s_np); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_ones); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = PyInt_FromSsize_t(__pyx_v_nrows); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_4);
  __pyx_t_4 = 0;
  __pyx_t_4 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_np); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_intc); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_6);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_dtype, __pyx_t_6) < 0) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
  __pyx_t_6 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_2, __pyx_t_4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 140, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_6);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  if (!(likely(((__pyx_t_6) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_6, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 140, __pyx_L1_error)
  __pyx_t_7 = ((PyArrayObject *)__pyx_t_6);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_used.rcbuffer->pybuffer, (PyObject*)__pyx_t_7, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_used = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_used.rcbuffer->pybuffer.buf = NULL;
      __PYX_ERR(0, 140, __pyx_L1_error)
    } else {__pyx_pybuffernd_used.diminfo[0].strides = __pyx_pybuffernd_used.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_used.diminfo[0].shape = __pyx_pybuffernd_used.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_7 = 0;
  __pyx_v_used = ((PyArrayObject *)__pyx_t_6);
  __pyx_t_6 = 0;
 141: 
 142:     # Release the GIL as we're in a thread-safe C function for most of our
 143:     # computation time.
+144:     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:;
      }
  }
+145:         birmingham_method_ext(
        birmingham_method_ext((&(*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_lines.data + __pyx_t_8 * __pyx_v_lines.strides[0]) ) + __pyx_t_9 * __pyx_v_lines.strides[1]) )))), __pyx_v_nrows, __pyx_v_ncols, (&(*__Pyx_BufPtrStrided1d(double *, __pyx_pybuffernd_location.rcbuffer->pybuffer.buf, __pyx_t_10, __pyx_pybuffernd_location.diminfo[0].strides))), (&(*__Pyx_BufPtrStrided1d(int *, __pyx_pybuffernd_used.rcbuffer->pybuffer.buf, __pyx_t_11, __pyx_pybuffernd_used.diminfo[0].strides))), __pyx_v_fopt);
      }
+146:             &lines[0, 0],
        __pyx_t_8 = 0;
        __pyx_t_9 = 0;
 147:             nrows,
 148:             ncols,
+149:             &location[0],
        __pyx_t_10 = 0;
+150:             &used[0],
        __pyx_t_11 = 0;
 151:             fopt
 152:         )
 153: 
+154:     return location, used
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_6 = PyTuple_New(2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 154, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_6);
  __Pyx_INCREF(((PyObject *)__pyx_v_location));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_location));
  PyTuple_SET_ITEM(__pyx_t_6, 0, ((PyObject *)__pyx_v_location));
  __Pyx_INCREF(((PyObject *)__pyx_v_used));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_used));
  PyTuple_SET_ITEM(__pyx_t_6, 1, ((PyObject *)__pyx_v_used));
  __pyx_r = __pyx_t_6;
  __pyx_t_6 = 0;
  goto __pyx_L0;
 155: 
 156: