Fix #5693: Implemented is_sorted in C #5719

Merged
merged 2 commits into from Dec 23, 2015
View
@@ -33,6 +33,7 @@
from matplotlib.markers import (
CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN,
CARETLEFTBASE, CARETRIGHTBASE, CARETUPBASE, CARETDOWNBASE)
+from matplotlib import _path
def segment_hits(cx, cy, x, y, radius):
@@ -702,9 +703,7 @@ def set_transform(self, t):
def _is_sorted(self, x):
"""return True if x is sorted in ascending order"""
# We don't handle the monotonically decreasing case.
- if len(x) < 2:
- return True
- return np.nanmin(x[1:] - x[:-1]) >= 0
+ return _path.is_sorted(x)
@allow_rasterization
def draw(self, renderer):
@@ -158,8 +158,9 @@ def test_marker_fill_styles():
def test_nan_is_sorted():
# Exercises issue from PR #2744 (NaN throwing warning in _is_sorted)
line = mlines.Line2D([],[])
- assert_true(line._is_sorted(np.array([1,2,3])))
- assert_true(not line._is_sorted(np.array([1,np.nan,3])))
+ assert_true(line._is_sorted(np.array([1, 2, 3])))
+ assert_true(line._is_sorted(np.array([1, np.nan, 3])))
+ assert_true(not line._is_sorted([3, 5] + [np.nan] * 100 + [0, 2]))
if __name__ == '__main__':
View
@@ -1183,4 +1183,70 @@ int convert_to_string(PathIterator &path,
}
+template<class T>
+struct _is_sorted
+{
+ bool operator()(PyArrayObject *array)
+ {
+ npy_intp size;
+ npy_intp i;
+ T last_value;
+ T current_value;
+
+ size = PyArray_DIM(array, 0);
+
+ for (i = 0; i < size; ++i) {
+ last_value = *((T *)PyArray_GETPTR1(array, i));
+ if (std::isfinite(last_value)) {
+ break;
+ }
+ }
+
+ if (i == size) {
+ // The whole array is non-finite
+ return false;
+ }
+
+ for (; i < size; ++i) {
+ current_value = *((T *)PyArray_GETPTR1(array, i));
+ if (std::isfinite(current_value)) {
+ if (current_value < last_value) {
+ return false;
+ }
+ last_value = current_value;
+ }
+ }
+
+ return true;
+ }
+};
+
+
+template<class T>
+struct _is_sorted_int
+{
+ bool operator()(PyArrayObject *array)
+ {
+ npy_intp size;
+ npy_intp i;
+ T last_value;
+ T current_value;
+
+ size = PyArray_DIM(array, 0);
+
+ last_value = *((T *)PyArray_GETPTR1(array, 0));
+
+ for (i = 1; i < size; ++i) {
+ current_value = *((T *)PyArray_GETPTR1(array, i));
+ if (current_value < last_value) {
+ return false;
+ }
+ last_value = current_value;
+ }
+
+ return true;
+ }
+};
+
+
#endif
View
@@ -726,6 +726,91 @@ static PyObject *Py_convert_to_string(PyObject *self, PyObject *args, PyObject *
return result;
}
+
+const char *Py_is_sorted__doc__ = "is_sorted(array)\n\n"
+ "Returns True if 1-D array is monotonically increasing, ignoring NaNs\n";
+
+static PyObject *Py_is_sorted(PyObject *self, PyObject *obj)
+{
+ npy_intp size;
+ bool result;
+
+ PyArrayObject *array = (PyArrayObject *)PyArray_FromAny(
+ obj, NULL, 1, 1, 0, NULL);
+
+ if (array == NULL) {
+ return NULL;
+ }
+
+ size = PyArray_DIM(array, 0);
+
+ if (size < 2) {
+ Py_DECREF(array);
+ Py_RETURN_TRUE;
+ }
+
+ /* Handle just the most common types here, otherwise coerce to
+ double */
+ switch(PyArray_TYPE(array)) {
+ case NPY_INT:
+ {
+ _is_sorted_int<npy_int> is_sorted;
+ result = is_sorted(array);
+ }
+ break;
+
+ case NPY_LONG:
+ {
+ _is_sorted_int<npy_long> is_sorted;
+ result = is_sorted(array);
+ }
+ break;
+
+ case NPY_LONGLONG:
+ {
+ _is_sorted_int<npy_longlong> is_sorted;
+ result = is_sorted(array);
+ }
+ break;
+
+ case NPY_FLOAT:
+ {
+ _is_sorted<npy_float> is_sorted;
+ result = is_sorted(array);
+ }
+ break;
+
+ case NPY_DOUBLE:
+ {
+ _is_sorted<npy_double> is_sorted;
+ result = is_sorted(array);
+ }
+ break;
+
+ default:
+ {
+ Py_DECREF(array);
+ array = (PyArrayObject *)PyArray_FromObject(obj, NPY_DOUBLE, 1, 1);
+
+ if (array == NULL) {
+ return NULL;
+ }
+
+ _is_sorted<npy_double> is_sorted;
+ result = is_sorted(array);
+ }
+ }
+
+ Py_DECREF(array);
+
+ if (result) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+
extern "C" {
static PyMethodDef module_functions[] = {
@@ -745,6 +830,7 @@ extern "C" {
{"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS, Py_convert_path_to_polygons__doc__},
{"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__},
{"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__},
+ {"is_sorted", (PyCFunction)Py_is_sorted, METH_O, Py_is_sorted__doc__},
{NULL}
};