Skip to content

Commit

Permalink
Merge pull request #17492 from aitikgupta/unwanted-mode-dep
Browse files Browse the repository at this point in the history
DEP: Shift correlate mode parsing to C and deprecate inexact matches
  • Loading branch information
mattip committed Mar 18, 2021
2 parents 9c68c2f + 89de3d9 commit ddbff08
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 20 deletions.
7 changes: 7 additions & 0 deletions 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.
6 changes: 6 additions & 0 deletions numpy/core/include/numpy/ndarraytypes.h
Expand Up @@ -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

Expand Down
13 changes: 0 additions & 13 deletions numpy/core/numeric.py
Expand Up @@ -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)

Expand Down Expand Up @@ -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)


Expand Down Expand Up @@ -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)


Expand Down
72 changes: 72 additions & 0 deletions numpy/core/src/multiarray/conversion_utils.c
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions numpy/core/src/multiarray/conversion_utils.h
Expand Up @@ -42,6 +42,9 @@ PyArray_TypestrConvert(int itemsize, int gentype);
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);

Expand Down
4 changes: 2 additions & 2 deletions numpy/core/src/multiarray/multiarraymodule.c
Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
42 changes: 37 additions & 5 deletions numpy/core/tests/test_numeric.py
Expand Up @@ -296,6 +296,7 @@ def test_var(self):
B[0] = 1j
assert_almost_equal(np.var(B), 0.25)


class TestIsscalar:
def test_isscalar(self):
assert_(np.isscalar(3.1))
Expand Down Expand Up @@ -2362,8 +2363,8 @@ def test_clip_property(self, data, shape):
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(
Expand Down Expand Up @@ -2896,10 +2897,10 @@ def _setup(self, dt):
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)
Expand Down Expand Up @@ -2947,6 +2948,22 @@ def test_zero_size(self):
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
Expand All @@ -2960,6 +2977,21 @@ def test_no_overwrite(self):
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:

Expand Down

0 comments on commit ddbff08

Please sign in to comment.