diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/einsumfunc.py | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arraytypes.c.src | 7 | ||||
-rw-r--r-- | numpy/core/src/multiarray/buffer.c | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/buffer.h | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.c | 10 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/descriptor.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/getset.c | 3 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 2 | ||||
-rw-r--r-- | numpy/ma/core.py | 23 | ||||
-rw-r--r-- | numpy/ma/tests/test_core.py | 150 |
13 files changed, 195 insertions, 18 deletions
diff --git a/numpy/core/einsumfunc.py b/numpy/core/einsumfunc.py index df2bf4a4e..c22f2da42 100644 --- a/numpy/core/einsumfunc.py +++ b/numpy/core/einsumfunc.py @@ -1028,7 +1028,7 @@ def einsum(*operands, **kwargs): Controls if intermediate optimization should occur. No optimization will occur if False and True will default to the 'greedy' algorithm. Also accepts an explicit contraction list from the ``np.einsum_path`` - function. See ``np.einsum_path`` for more details. Default is True. + function. See ``np.einsum_path`` for more details. Defaults to False. Returns ------- diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index 41748c714..8ba3f5310 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -471,7 +471,7 @@ array_dealloc(PyArrayObject *self) { PyArrayObject_fields *fa = (PyArrayObject_fields *)self; - _array_dealloc_buffer_info(self); + _dealloc_cached_buffer_info((PyObject*)self); if (fa->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *)self); diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 1055f6adc..553737a3a 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -2,7 +2,8 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "structmember.h" - +#include <limits.h> +#include <assert.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE @@ -34,8 +35,7 @@ #include "cblasfuncs.h" #include "npy_cblas.h" -#include <limits.h> -#include <assert.h> +#include "buffer.h" /* check for sequences, but ignore the types numpy considers scalars */ static NPY_INLINE npy_bool @@ -947,6 +947,7 @@ VOID_setitem(PyObject *op, void *input, void *vap) memset(ip + view.len, 0, itemsize - view.len); } PyBuffer_Release(&view); + _dealloc_cached_buffer_info(op); } #else { diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c index c8e3da8bc..663930337 100644 --- a/numpy/core/src/multiarray/buffer.c +++ b/numpy/core/src/multiarray/buffer.c @@ -884,7 +884,7 @@ fail: */ NPY_NO_EXPORT void -_array_dealloc_buffer_info(PyArrayObject *self) +_dealloc_cached_buffer_info(PyObject *self) { int reset_error_state = 0; PyObject *ptype, *pvalue, *ptraceback; @@ -898,7 +898,7 @@ _array_dealloc_buffer_info(PyArrayObject *self) PyErr_Fetch(&ptype, &pvalue, &ptraceback); } - _buffer_clear_info((PyObject*)self); + _buffer_clear_info(self); if (reset_error_state) { PyErr_Restore(ptype, pvalue, ptraceback); diff --git a/numpy/core/src/multiarray/buffer.h b/numpy/core/src/multiarray/buffer.h index d5da8f440..fae413c85 100644 --- a/numpy/core/src/multiarray/buffer.h +++ b/numpy/core/src/multiarray/buffer.h @@ -4,7 +4,7 @@ extern NPY_NO_EXPORT PyBufferProcs array_as_buffer; NPY_NO_EXPORT void -_array_dealloc_buffer_info(PyArrayObject *self); +_dealloc_cached_buffer_info(PyObject *self); NPY_NO_EXPORT PyArray_Descr* _descriptor_from_pep3118_format(char *s); diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index c70f8526e..c6f4847b3 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -311,6 +311,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, PyErr_Clear(); dtype = _descriptor_from_pep3118_format(buffer_view.format); PyBuffer_Release(&buffer_view); + _dealloc_cached_buffer_info(obj); if (dtype) { goto promote_types; } @@ -322,6 +323,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, dtype = PyArray_DescrNewFromType(NPY_VOID); dtype->elsize = buffer_view.itemsize; PyBuffer_Release(&buffer_view); + _dealloc_cached_buffer_info(obj); goto promote_types; } else { @@ -679,6 +681,14 @@ _IsWriteable(PyArrayObject *ap) return NPY_FALSE; } PyBuffer_Release(&view); + /* + * The first call to PyObject_GetBuffer stores a reference to a struct + * _buffer_info_t (from buffer.c, with format, ndim, strides and shape) in + * a static dictionary, with id(base) as the key. Usually we release it + * after the call to PyBuffer_Release, via a call to + * _dealloc_cached_buffer_info, but in this case leave it in the cache to + * speed up future calls to _IsWriteable. + */ #else if (PyObject_AsWriteBuffer(base, &dummy, &n) < 0) { PyErr_Clear(); diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 7e92e5991..cef3c27ed 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -16,6 +16,7 @@ #include "conversion_utils.h" #include "alloc.h" +#include "buffer.h" static int PyArray_PyIntAsInt_ErrMsg(PyObject *o, const char * msg) NPY_GCC_NONNULL(2); @@ -185,6 +186,7 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) * sticks around after the release. */ PyBuffer_Release(&view); + _dealloc_cached_buffer_info(obj); /* Point to the base of the buffer object if present */ if (PyMemoryView_Check(obj)) { diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 28a414892..3c61f3b2b 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -744,12 +744,14 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, d[i] = buffer_view.shape[i]; } PyBuffer_Release(&buffer_view); + _dealloc_cached_buffer_info(obj); return 0; } else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { d[0] = buffer_view.len; *maxndim = 1; PyBuffer_Release(&buffer_view); + _dealloc_cached_buffer_info(obj); return 0; } else { @@ -2470,6 +2472,7 @@ PyArray_FromInterface(PyObject *origin) * sticks around after the release. */ PyBuffer_Release(&view); + _dealloc_cached_buffer_info(base); #else res = PyObject_AsWriteBuffer(base, (void **)&data, &buffer_len); if (res < 0) { @@ -3725,6 +3728,7 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, * sticks around after the release. */ PyBuffer_Release(&view); + _dealloc_cached_buffer_info(buf); #else if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts) == -1) { writeable = 0; diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index a0dc98f0e..e3a018356 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -19,6 +19,7 @@ #include "descriptor.h" #include "alloc.h" #include "assert.h" +#include "buffer.h" /* * offset: A starting offset. @@ -1764,6 +1765,7 @@ arraydescr_dealloc(PyArray_Descr *self) Py_INCREF(self); return; } + _dealloc_cached_buffer_info((PyObject*)self); Py_XDECREF(self->typeobj); Py_XDECREF(self->names); Py_XDECREF(self->fields); diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index cae4273ff..24962da8a 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -20,6 +20,7 @@ #include "arrayobject.h" #include "mem_overlap.h" #include "alloc.h" +#include "buffer.h" /******************* array attribute get and set routines ******************/ @@ -143,6 +144,7 @@ array_strides_set(PyArrayObject *self, PyObject *obj) offset = PyArray_BYTES(self) - (char *)view.buf; numbytes = view.len + offset; PyBuffer_Release(&view); + _dealloc_cached_buffer_info((PyObject*)new); } #else if (PyArray_BASE(new) && @@ -376,6 +378,7 @@ array_data_set(PyArrayObject *self, PyObject *op) * sticks around after the release. */ PyBuffer_Release(&view); + _dealloc_cached_buffer_info(op); #else if (PyObject_AsWriteBuffer(op, &buf, &buf_len) < 0) { PyErr_Clear(); diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index a32aa47ab..0d7db2d8f 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -139,6 +139,7 @@ gentype_alloc(PyTypeObject *type, Py_ssize_t nitems) static void gentype_dealloc(PyObject *v) { + _dealloc_cached_buffer_info(v); Py_TYPE(v)->tp_free(v); } @@ -1863,6 +1864,7 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) * sticks around after the release. */ PyBuffer_Release(&view); + _dealloc_cached_buffer_info(self); } else { Py_DECREF(ret); diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 52039bd97..a8cf02336 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -449,6 +449,7 @@ def _check_fill_value(fill_value, ndtype): If fill_value is not None, its value is forced to the given dtype. The result is always a 0d array. + """ ndtype = np.dtype(ndtype) fields = ndtype.fields @@ -468,17 +469,19 @@ def _check_fill_value(fill_value, ndtype): dtype=ndtype) else: if isinstance(fill_value, basestring) and (ndtype.char not in 'OSVU'): + # Note this check doesn't work if fill_value is not a scalar err_msg = "Cannot set fill value of string with array of dtype %s" raise TypeError(err_msg % ndtype) else: # In case we want to convert 1e20 to int. + # Also in case of converting string arrays. try: fill_value = np.array(fill_value, copy=False, dtype=ndtype) - except OverflowError: - # Raise TypeError instead of OverflowError. OverflowError - # is seldom used, and the real problem here is that the - # passed fill_value is not compatible with the ndtype. - err_msg = "Fill value %s overflows dtype %s" + except (OverflowError, ValueError): + # Raise TypeError instead of OverflowError or ValueError. + # OverflowError is seldom used, and the real problem here is + # that the passed fill_value is not compatible with the ndtype. + err_msg = "Cannot convert fill_value %s to dtype %s" raise TypeError(err_msg % (fill_value, ndtype)) return np.array(fill_value) @@ -4017,6 +4020,16 @@ class MaskedArray(ndarray): check = check.view(type(self)) check._update_from(self) check._mask = mask + + # Cast fill value to bool_ if needed. If it cannot be cast, the + # default boolean fill value is used. + if check._fill_value is not None: + try: + fill = _check_fill_value(check._fill_value, np.bool_) + except (TypeError, ValueError): + fill = _check_fill_value(None, np.bool_) + check._fill_value = fill + return check def __eq__(self, other): diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index e935b65e3..a39e7dd3d 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -60,6 +60,11 @@ suppress_copy_mask_on_assignment.filter( "setting an item on a masked array which has a shared mask will not copy") +# For parametrized numeric testing +num_dts = [np.dtype(dt_) for dt_ in '?bhilqBHILQefdgFD'] +num_ids = [dt_.char for dt_ in num_dts] + + class TestMaskedArray(object): # Base test class for MaskedArrays. @@ -1413,23 +1418,34 @@ class TestMaskedArrayArithmetic(object): # Test the equality of structured arrays ndtype = [('A', int), ('B', int)] a = array([(1, 1), (2, 2)], mask=[(0, 1), (0, 0)], dtype=ndtype) + test = (a == a) assert_equal(test.data, [True, True]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + test = (a == a[0]) assert_equal(test.data, [True, False]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(1, 0), (0, 0)], dtype=ndtype) test = (a == b) assert_equal(test.data, [False, True]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + test = (a[0] == b) assert_equal(test.data, [False, False]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(0, 1), (1, 0)], dtype=ndtype) test = (a == b) assert_equal(test.data, [True, True]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + # complicated dtype, 2-dimensional array. ndtype = [('A', int), ('B', [('BA', int), ('BB', int)])] a = array([[(1, (1, 1)), (2, (2, 2))], @@ -1439,28 +1455,40 @@ class TestMaskedArrayArithmetic(object): test = (a[0, 0] == a) assert_equal(test.data, [[True, False], [False, False]]) assert_equal(test.mask, [[False, False], [False, True]]) + assert_(test.fill_value == True) def test_ne_on_structured(self): # Test the equality of structured arrays ndtype = [('A', int), ('B', int)] a = array([(1, 1), (2, 2)], mask=[(0, 1), (0, 0)], dtype=ndtype) + test = (a != a) assert_equal(test.data, [False, False]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + test = (a != a[0]) assert_equal(test.data, [False, True]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(1, 0), (0, 0)], dtype=ndtype) test = (a != b) assert_equal(test.data, [True, False]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + test = (a[0] != b) assert_equal(test.data, [True, True]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(0, 1), (1, 0)], dtype=ndtype) test = (a != b) assert_equal(test.data, [False, False]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + # complicated dtype, 2-dimensional array. ndtype = [('A', int), ('B', [('BA', int), ('BB', int)])] a = array([[(1, (1, 1)), (2, (2, 2))], @@ -1470,6 +1498,7 @@ class TestMaskedArrayArithmetic(object): test = (a[0, 0] != a) assert_equal(test.data, [[False, True], [True, True]]) assert_equal(test.mask, [[False, False], [False, True]]) + assert_(test.fill_value == True) def test_eq_ne_structured_extra(self): # ensure simple examples are symmetric and make sense. @@ -1505,6 +1534,120 @@ class TestMaskedArrayArithmetic(object): el_by_el = [m1[name] != m2[name] for name in dt.names] assert_equal(array(el_by_el, dtype=bool).any(), ne_expected) + @pytest.mark.parametrize('dt', ['S', 'U']) + @pytest.mark.parametrize('fill', [None, 'A']) + def test_eq_for_strings(self, dt, fill): + # Test the equality of structured arrays + a = array(['a', 'b'], dtype=dt, mask=[0, 1], fill_value=fill) + + test = (a == a) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a == a[0]) + assert_equal(test.data, [True, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array(['a', 'b'], dtype=dt, mask=[1, 0], fill_value=fill) + test = (a == b) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + # test = (a[0] == b) # doesn't work in Python2 + test = (b == a[0]) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + @pytest.mark.parametrize('dt', ['S', 'U']) + @pytest.mark.parametrize('fill', [None, 'A']) + def test_ne_for_strings(self, dt, fill): + # Test the equality of structured arrays + a = array(['a', 'b'], dtype=dt, mask=[0, 1], fill_value=fill) + + test = (a != a) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a != a[0]) + assert_equal(test.data, [False, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array(['a', 'b'], dtype=dt, mask=[1, 0], fill_value=fill) + test = (a != b) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + # test = (a[0] != b) # doesn't work in Python2 + test = (b != a[0]) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + @pytest.mark.parametrize('dt1', num_dts, ids=num_ids) + @pytest.mark.parametrize('dt2', num_dts, ids=num_ids) + @pytest.mark.parametrize('fill', [None, 1]) + def test_eq_for_numeric(self, dt1, dt2, fill): + # Test the equality of structured arrays + a = array([0, 1], dtype=dt1, mask=[0, 1], fill_value=fill) + + test = (a == a) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a == a[0]) + assert_equal(test.data, [True, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array([0, 1], dtype=dt2, mask=[1, 0], fill_value=fill) + test = (a == b) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + # test = (a[0] == b) # doesn't work in Python2 + test = (b == a[0]) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + @pytest.mark.parametrize('dt1', num_dts, ids=num_ids) + @pytest.mark.parametrize('dt2', num_dts, ids=num_ids) + @pytest.mark.parametrize('fill', [None, 1]) + def test_ne_for_numeric(self, dt1, dt2, fill): + # Test the equality of structured arrays + a = array([0, 1], dtype=dt1, mask=[0, 1], fill_value=fill) + + test = (a != a) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a != a[0]) + assert_equal(test.data, [False, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array([0, 1], dtype=dt2, mask=[1, 0], fill_value=fill) + test = (a != b) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + # test = (a[0] != b) # doesn't work in Python2 + test = (b != a[0]) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + def test_eq_with_None(self): # Really, comparisons with None should not be done, but check them # anyway. Note that pep8 will flag these tests. @@ -5019,11 +5162,8 @@ def test_astype_mask_ordering(): assert_(x_f2.mask.flags.f_contiguous) -dts = [np.dtype(dt_) for dt_ in '?bhilqBHILQefdgFD'] -ids = [dt_.char for dt_ in dts] - -@pytest.mark.parametrize('dt1', dts, ids=ids) -@pytest.mark.parametrize('dt2', dts, ids=ids) +@pytest.mark.parametrize('dt1', num_dts, ids=num_ids) +@pytest.mark.parametrize('dt2', num_dts, ids=num_ids) @pytest.mark.filterwarnings('ignore::numpy.ComplexWarning') def test_astype_basic(dt1, dt2): # See gh-12070 |