summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2021-03-18 22:48:42 +0200
committerGitHub <noreply@github.com>2021-03-18 22:48:42 +0200
commitddbff082e0bab0e3580be9f2f275418595879022 (patch)
tree4a9859df268db7e5d35f9bc1087cedc32b7888ef
parent9c68c2f7b1b2128a3b4af2134565f60d286fa8b9 (diff)
parent89de3d9d21e38e4070681e455d98575df3f3b51f (diff)
downloadnumpy-ddbff082e0bab0e3580be9f2f275418595879022.tar.gz
Merge pull request #17492 from aitikgupta/unwanted-mode-dep
DEP: Shift correlate mode parsing to C and deprecate inexact matches
-rw-r--r--doc/release/upcoming_changes/17492.deprecation.rst7
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h6
-rw-r--r--numpy/core/numeric.py13
-rw-r--r--numpy/core/src/multiarray/conversion_utils.c72
-rw-r--r--numpy/core/src/multiarray/conversion_utils.h3
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c4
-rw-r--r--numpy/core/tests/test_numeric.py42
7 files changed, 127 insertions, 20 deletions
diff --git a/doc/release/upcoming_changes/17492.deprecation.rst b/doc/release/upcoming_changes/17492.deprecation.rst
new file mode 100644
index 000000000..50005aed7
--- /dev/null
+++ b/doc/release/upcoming_changes/17492.deprecation.rst
@@ -0,0 +1,7 @@
+Inexact matches for `numpy.convolve` and `numpy.correlate` are deprecated
+-------------------------------------------------------------------------
+
+`numpy.convolve` and `numpy.correlate` now emits a warning when there are case
+insensitive and/or inexact matches found for ``mode`` argument in the functions.
+Pass full ``"same"``, ``"valid"``, ``"full"`` strings instead of
+``"s"``, ``"v"``, ``"f"`` for the ``mode`` argument.
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index a731e7f15..dacb72022 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -236,6 +236,12 @@ typedef enum {
NPY_RAISE=2
} NPY_CLIPMODE;
+typedef enum {
+ NPY_VALID=0,
+ NPY_SAME=1,
+ NPY_FULL=2
+} NPY_CORRELATEMODE;
+
/* The special not-a-time (NaT) value */
#define NPY_DATETIME_NAT NPY_MIN_INT64
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index 7675386e7..a6ee9eba9 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -662,17 +662,6 @@ def flatnonzero(a):
return np.nonzero(np.ravel(a))[0]
-_mode_from_name_dict = {'v': 0,
- 's': 1,
- 'f': 2}
-
-
-def _mode_from_name(mode):
- if isinstance(mode, str):
- return _mode_from_name_dict[mode.lower()[0]]
- return mode
-
-
def _correlate_dispatcher(a, v, mode=None):
return (a, v)
@@ -748,7 +737,6 @@ def correlate(a, v, mode='valid'):
array([ 0.0+0.j , 3.0+1.j , 1.5+1.5j, 1.0+0.j , 0.5+0.5j])
"""
- mode = _mode_from_name(mode)
return multiarray.correlate2(a, v, mode)
@@ -852,7 +840,6 @@ def convolve(a, v, mode='full'):
raise ValueError('a cannot be empty')
if len(v) == 0:
raise ValueError('v cannot be empty')
- mode = _mode_from_name(mode)
return multiarray.correlate(a, v[::-1], mode)
diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c
index dd18f71fd..3c4c21ded 100644
--- a/numpy/core/src/multiarray/conversion_utils.c
+++ b/numpy/core/src/multiarray/conversion_utils.c
@@ -715,6 +715,78 @@ PyArray_ConvertClipmodeSequence(PyObject *object, NPY_CLIPMODE *modes, int n)
return NPY_SUCCEED;
}
+static int correlatemode_parser(char const *str, Py_ssize_t length, void *data)
+{
+ NPY_CORRELATEMODE *val = (NPY_CORRELATEMODE *)data;
+ int is_exact = 0;
+
+ if (length < 1) {
+ return -1;
+ }
+ if (str[0] == 'V' || str[0] == 'v') {
+ *val = NPY_VALID;
+ is_exact = (length == 5 && strcmp(str, "valid") == 0);
+ }
+ else if (str[0] == 'S' || str[0] == 's') {
+ *val = NPY_SAME;
+ is_exact = (length == 4 && strcmp(str, "same") == 0);
+ }
+ else if (str[0] == 'F' || str[0] == 'f') {
+ *val = NPY_FULL;
+ is_exact = (length == 4 && strcmp(str, "full") == 0);
+ }
+ else {
+ return -1;
+ }
+
+ /* Filters out the case sensitive/non-exact
+ * match inputs and other inputs and outputs DeprecationWarning
+ */
+ if (!is_exact) {
+ /* Numpy 1.21, 2021-01-19 */
+ if (DEPRECATE("inexact matches and case insensitive matches for "
+ "convolve/correlate mode are deprecated, please "
+ "use one of 'valid', 'same', or 'full' instead.") < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Convert an object to NPY_VALID / NPY_SAME / NPY_FULL
+ */
+NPY_NO_EXPORT int
+PyArray_CorrelatemodeConverter(PyObject *object, NPY_CORRELATEMODE *val)
+{
+ if (PyUnicode_Check(object)) {
+ return string_converter_helper(
+ object, (void *)val, correlatemode_parser, "mode",
+ "must be one of 'valid', 'same', or 'full'");
+ }
+
+ else {
+ /* For users passing integers */
+ int number = PyArray_PyIntAsInt(object);
+ if (error_converting(number)) {
+ PyErr_SetString(PyExc_TypeError,
+ "convolve/correlate mode not understood");
+ return NPY_FAIL;
+ }
+ if (number <= (int) NPY_FULL
+ && number >= (int) NPY_VALID) {
+ *val = (NPY_CORRELATEMODE) number;
+ return NPY_SUCCEED;
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "integer convolve/correlate mode must be 0, 1, or 2");
+ return NPY_FAIL;
+ }
+ }
+}
+
static int casting_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_CASTING *casting = (NPY_CASTING *)data;
diff --git a/numpy/core/src/multiarray/conversion_utils.h b/numpy/core/src/multiarray/conversion_utils.h
index bee0c6064..7d1871c43 100644
--- a/numpy/core/src/multiarray/conversion_utils.h
+++ b/numpy/core/src/multiarray/conversion_utils.h
@@ -43,6 +43,9 @@ NPY_NO_EXPORT PyObject *
PyArray_IntTupleFromIntp(int len, npy_intp const *vals);
NPY_NO_EXPORT int
+PyArray_CorrelatemodeConverter(PyObject *object, NPY_CORRELATEMODE *val);
+
+NPY_NO_EXPORT int
PyArray_SelectkindConverter(PyObject *obj, NPY_SELECTKIND *selectkind);
/*
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index a0f7afeb5..12705dc19 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -2839,7 +2839,7 @@ array_correlate(PyObject *NPY_UNUSED(dummy),
if (npy_parse_arguments("correlate", args, len_args, kwnames,
"a", NULL, &a0,
"v", NULL, &shape,
- "|mode", &PyArray_PythonPyIntFromInt, &mode,
+ "|mode", &PyArray_CorrelatemodeConverter, &mode,
NULL, NULL, NULL) < 0) {
return NULL;
}
@@ -2857,7 +2857,7 @@ array_correlate2(PyObject *NPY_UNUSED(dummy),
if (npy_parse_arguments("correlate2", args, len_args, kwnames,
"a", NULL, &a0,
"v", NULL, &shape,
- "|mode", &PyArray_PythonPyIntFromInt, &mode,
+ "|mode", &PyArray_CorrelatemodeConverter, &mode,
NULL, NULL, NULL) < 0) {
return NULL;
}
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index 8d3cec708..aba90ece5 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -296,6 +296,7 @@ class TestNonarrayArgs:
B[0] = 1j
assert_almost_equal(np.var(B), 0.25)
+
class TestIsscalar:
def test_isscalar(self):
assert_(np.isscalar(3.1))
@@ -2362,8 +2363,8 @@ class TestClip:
base_shape=shape,
# Commenting out the min_dims line allows zero-dimensional arrays,
# and zero-dimensional arrays containing NaN make the test fail.
- min_dims=1
-
+ min_dims=1
+
)
)
amin = data.draw(
@@ -2896,10 +2897,10 @@ class TestCorrelate:
self.x = np.array([1, 2, 3, 4, 5], dtype=dt)
self.xs = np.arange(1, 20)[::3]
self.y = np.array([-1, -2, -3], dtype=dt)
- self.z1 = np.array([ -3., -8., -14., -20., -26., -14., -5.], dtype=dt)
+ self.z1 = np.array([-3., -8., -14., -20., -26., -14., -5.], dtype=dt)
self.z1_4 = np.array([-2., -5., -8., -11., -14., -5.], dtype=dt)
- self.z1r = np.array([-15., -22., -22., -16., -10., -4., -1.], dtype=dt)
- self.z2 = np.array([-5., -14., -26., -20., -14., -8., -3.], dtype=dt)
+ self.z1r = np.array([-15., -22., -22., -16., -10., -4., -1.], dtype=dt)
+ self.z2 = np.array([-5., -14., -26., -20., -14., -8., -3.], dtype=dt)
self.z2r = np.array([-1., -4., -10., -16., -22., -22., -15.], dtype=dt)
self.zs = np.array([-3., -14., -30., -48., -66., -84.,
-102., -54., -19.], dtype=dt)
@@ -2947,6 +2948,22 @@ class TestCorrelate:
with pytest.raises(ValueError):
np.correlate(np.ones(1000), np.array([]), mode='full')
+ def test_mode(self):
+ d = np.ones(100)
+ k = np.ones(3)
+ default_mode = np.correlate(d, k, mode='valid')
+ with assert_warns(DeprecationWarning):
+ valid_mode = np.correlate(d, k, mode='v')
+ assert_array_equal(valid_mode, default_mode)
+ # integer mode
+ with assert_raises(ValueError):
+ np.correlate(d, k, mode=-1)
+ assert_array_equal(np.correlate(d, k, mode=0), valid_mode)
+ # illegal arguments
+ with assert_raises(TypeError):
+ np.correlate(d, k, mode=None)
+
+
class TestConvolve:
def test_object(self):
d = [1.] * 100
@@ -2960,6 +2977,21 @@ class TestConvolve:
assert_array_equal(d, np.ones(100))
assert_array_equal(k, np.ones(3))
+ def test_mode(self):
+ d = np.ones(100)
+ k = np.ones(3)
+ default_mode = np.convolve(d, k, mode='full')
+ with assert_warns(DeprecationWarning):
+ full_mode = np.convolve(d, k, mode='f')
+ assert_array_equal(full_mode, default_mode)
+ # integer mode
+ with assert_raises(ValueError):
+ np.convolve(d, k, mode=-1)
+ assert_array_equal(np.convolve(d, k, mode=2), full_mode)
+ # illegal arguments
+ with assert_raises(TypeError):
+ np.convolve(d, k, mode=None)
+
class TestArgwhere: