From 6a119c982463097aebc8d004fc731b424721a650 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 17:39:11 +0000 Subject: [PATCH 1/7] BUG: Be explicit about allowing all integer types in matrix exponentiation Fixes gh-9506, unsigned exponentiation --- numpy/matrixlib/defmatrix.py | 2 +- numpy/matrixlib/tests/test_defmatrix.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/numpy/matrixlib/defmatrix.py b/numpy/matrixlib/defmatrix.py index 2aed3ebde03c..e016b5f4c5aa 100644 --- a/numpy/matrixlib/defmatrix.py +++ b/numpy/matrixlib/defmatrix.py @@ -137,7 +137,7 @@ def matrix_power(M, n): M = asanyarray(M) if M.ndim != 2 or M.shape[0] != M.shape[1]: raise ValueError("input must be a square array") - if not issubdtype(type(n), int): + if not issubdtype(type(n), N.integer): raise TypeError("exponent must be an integer") from numpy.linalg import inv diff --git a/numpy/matrixlib/tests/test_defmatrix.py b/numpy/matrixlib/tests/test_defmatrix.py index e4c3c49fbbd9..77f262031599 100644 --- a/numpy/matrixlib/tests/test_defmatrix.py +++ b/numpy/matrixlib/tests/test_defmatrix.py @@ -249,6 +249,12 @@ def test_pow(self): assert_array_almost_equal(m4, np.dot(m2, m2)) assert_array_almost_equal(np.dot(mi, m), np.eye(2)) + def test_scalar_type_pow(self): + m = matrix([[1, 2], [3, 4]]) + for scalar_t in [np.int8, np.uint8]: + two = scalar_t(2) + assert_array_almost_equal(m ** 2, m ** two) + def test_notimplemented(self): '''Check that 'not implemented' operations produce a failure.''' A = matrix([[1., 2.], From fb004f8bc610b0cf52342d35314a9e0a0434e745 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 17:40:56 +0000 Subject: [PATCH 2/7] MAINT: Stop using the undocumented coercion-then-downcast feature of subdtype --- numpy/core/function_base.py | 2 +- numpy/doc/basics.py | 4 ++-- numpy/lib/_iotools.py | 2 +- numpy/lib/tests/test_function_base.py | 8 ++++---- numpy/lib/tests/test_index_tricks.py | 2 +- numpy/lib/tests/test_type_check.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py index 7098b8b91428..0415e16ac64b 100644 --- a/numpy/core/function_base.py +++ b/numpy/core/function_base.py @@ -339,7 +339,7 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None): # complex and another is negative and log would produce NaN otherwise start = start + (stop - stop) stop = stop + (start - start) - if _nx.issubdtype(dtype, complex): + if _nx.issubdtype(dtype, _nx.complexfloating): start = start + 0j stop = stop + 0j diff --git a/numpy/doc/basics.py b/numpy/doc/basics.py index 083d55a84cb6..10d7248c179e 100644 --- a/numpy/doc/basics.py +++ b/numpy/doc/basics.py @@ -114,10 +114,10 @@ >>> d dtype('int32') - >>> np.issubdtype(d, int) + >>> np.issubdtype(d, np.integer) True - >>> np.issubdtype(d, float) + >>> np.issubdtype(d, np.floating) False diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py index 304bba3d3a5c..1874c2e97eab 100644 --- a/numpy/lib/_iotools.py +++ b/numpy/lib/_iotools.py @@ -527,7 +527,7 @@ class StringConverter(object): _mapper.append((nx.int64, int, -1)) _mapper.extend([(nx.floating, float, nx.nan), - (complex, _bytes_to_complex, nx.nan + 0j), + (nx.complexfloating, _bytes_to_complex, nx.nan + 0j), (nx.longdouble, nx.longdouble, nx.nan), (nx.string_, bytes, b'???')]) diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index ad840f8ef7f3..151c20a4bc9f 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1711,16 +1711,16 @@ def test_type(self): # Check the type of the returned histogram a = np.arange(10) + .5 h, b = histogram(a) - assert_(np.issubdtype(h.dtype, int)) + assert_(np.issubdtype(h.dtype, np.integer)) h, b = histogram(a, normed=True) - assert_(np.issubdtype(h.dtype, float)) + assert_(np.issubdtype(h.dtype, np.floating)) h, b = histogram(a, weights=np.ones(10, int)) - assert_(np.issubdtype(h.dtype, int)) + assert_(np.issubdtype(h.dtype, np.integer)) h, b = histogram(a, weights=np.ones(10, float)) - assert_(np.issubdtype(h.dtype, float)) + assert_(np.issubdtype(h.dtype, np.floating)) def test_f32_rounding(self): # gh-4799, check that the rounding of the edges works with float32 diff --git a/numpy/lib/tests/test_index_tricks.py b/numpy/lib/tests/test_index_tricks.py index f06406c9e065..452b3d6a2d88 100644 --- a/numpy/lib/tests/test_index_tricks.py +++ b/numpy/lib/tests/test_index_tricks.py @@ -243,7 +243,7 @@ def test_shape_and_dtype(self): for k, (a, sz) in enumerate(zip(arrays, sizes)): assert_equal(a.shape[k], sz) assert_(all(sh == 1 for j, sh in enumerate(a.shape) if j != k)) - assert_(np.issubdtype(a.dtype, int)) + assert_(np.issubdtype(a.dtype, np.integer)) def test_bool(self): bool_a = [True, False, True, True] diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py index d863e5924ea0..e05079981d37 100644 --- a/numpy/lib/tests/test_type_check.py +++ b/numpy/lib/tests/test_type_check.py @@ -420,7 +420,7 @@ class TestArrayConversion(object): def test_asfarray(self): a = asfarray(np.array([1, 2, 3])) assert_equal(a.__class__, np.ndarray) - assert_(np.issubdtype(a.dtype, float)) + assert_(np.issubdtype(a.dtype, np.floating)) if __name__ == "__main__": run_module_suite() From 0ab1a2b12baabe3bd7063e2963c83482a4f57016 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 17:41:28 +0000 Subject: [PATCH 3/7] MAINT: Simplify test for string-likes --- numpy/core/numeric.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 13a99505c4ea..7d53f2b68855 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -436,8 +436,7 @@ def count_nonzero(a, axis=None): if issubdtype(a.dtype, np.number): return (a != 0).sum(axis=axis, dtype=np.intp) - if (issubdtype(a.dtype, np.string_) or - issubdtype(a.dtype, np.unicode_)): + if issubdtype(a.dtype, np.character): nullstr = a.dtype.type('') return (a != nullstr).sum(axis=axis, dtype=np.intp) From dda20349d172ed09b949a3b819eb16a384cd29a8 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 18:08:57 +0000 Subject: [PATCH 4/7] BUG: abstract types did not compare correctly with issubdtype --- numpy/core/numerictypes.py | 6 ++++-- numpy/core/tests/test_numerictypes.py | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index 136081412ffc..e7e7e55093c2 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -751,14 +751,16 @@ def issubdtype(arg1, arg2): False """ + if not issubclass_(arg1, generic): + arg1 = dtype(arg1).type if issubclass_(arg2, generic): - return issubclass(dtype(arg1).type, arg2) + return issubclass(arg1, arg2) mro = dtype(arg2).type.mro() if len(mro) > 1: val = mro[1] else: val = mro[0] - return issubclass(dtype(arg1).type, val) + return issubclass(arg1, val) # This dictionary allows look up based on any alias for an array data-type diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py index f912e39447eb..977e4ce40b49 100644 --- a/numpy/core/tests/test_numerictypes.py +++ b/numpy/core/tests/test_numerictypes.py @@ -377,5 +377,12 @@ def test_return(self): res = self.ary[['f0', 'f2']].tolist() assert_(res == [(1, 3), (5, 7)]) + +class TestIsSubDType(object): + def test_both_abstract(self): + assert_(np.issubdtype(np.floating, np.inexact)) + assert_(not np.issubdtype(np.inexact, np.floating)) + + if __name__ == "__main__": run_module_suite() From 44a4b719e49b7106560b46d373b8627ab19e7c2d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 18:14:06 +0000 Subject: [PATCH 5/7] BUG: dtypes are ignored and downcast in issubdtype Fixes #9480 --- numpy/core/numerictypes.py | 19 +++++++++++-------- numpy/core/tests/test_numerictypes.py | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index e7e7e55093c2..941642d9125c 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -753,14 +753,17 @@ def issubdtype(arg1, arg2): """ if not issubclass_(arg1, generic): arg1 = dtype(arg1).type - if issubclass_(arg2, generic): - return issubclass(arg1, arg2) - mro = dtype(arg2).type.mro() - if len(mro) > 1: - val = mro[1] - else: - val = mro[0] - return issubclass(arg1, val) + if not issubclass_(arg2, generic): + arg2_orig = arg2 + arg2 = dtype(arg2).type + if not isinstance(arg2_orig, dtype): + # weird deprecated behaviour, that tried to infer np.floating from + # float, and similar less obvious things, such as np.generic from + # basestring + mro = arg2.mro() + arg2 = mro[1] if len(mro) > 1 else mro[0] + + return issubclass(arg1, arg2) # This dictionary allows look up based on any alias for an array data-type diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py index 977e4ce40b49..8831cd1bb943 100644 --- a/numpy/core/tests/test_numerictypes.py +++ b/numpy/core/tests/test_numerictypes.py @@ -1,6 +1,7 @@ from __future__ import division, absolute_import, print_function import sys +import itertools import numpy as np from numpy.testing import ( @@ -379,10 +380,34 @@ def test_return(self): class TestIsSubDType(object): + # scalar types can be promoted into dtypes + wrappers = [np.dtype, lambda x: x] + def test_both_abstract(self): assert_(np.issubdtype(np.floating, np.inexact)) assert_(not np.issubdtype(np.inexact, np.floating)) + def test_same(self): + for cls in (np.float32, np.int32): + for w1, w2 in itertools.product(self.wrappers, repeat=2): + assert_(np.issubdtype(w1(cls), w2(cls))) + + def test_subclass(self): + # note we cannot promote floating to a dtype, as it would turn into a + # concrete type + for w in self.wrappers: + assert_(np.issubdtype(w(np.float32), np.floating)) + assert_(np.issubdtype(w(np.float64), np.floating)) + + def test_subclass_backwards(self): + for w in self.wrappers: + assert_(not np.issubdtype(np.floating, w(np.float32))) + assert_(not np.issubdtype(np.floating, w(np.float64))) + + def test_sibling_class(self): + for w1, w2 in itertools.product(self.wrappers, repeat=2): + assert_(not np.issubdtype(w1(np.float32), w2(np.float64))) + assert_(not np.issubdtype(w1(np.float64), w2(np.float32))) if __name__ == "__main__": run_module_suite() From 4cac8cba65b343f845a7068407997105cca7bfc9 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 18:14:53 +0000 Subject: [PATCH 6/7] DOC: Make example work in python 3 --- numpy/core/numerictypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index 941642d9125c..fbe89b226fa4 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -745,7 +745,7 @@ def issubdtype(arg1, arg2): Examples -------- - >>> np.issubdtype('S1', str) + >>> np.issubdtype('S1', np.string_) True >>> np.issubdtype(np.float64, np.float32) False From 091b8c3b16d39d18f6921f9a17d06c1652b40dca Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 1 Aug 2017 18:15:29 +0000 Subject: [PATCH 7/7] DEP: Deprecate the coercion of dtype-likes to their superclass In many cases, this coercion is surprising, or would be if the user knew about it: * [('a', int)] -> np.flexible * str - > str (!) - not even a numpy type * 'float32' -> np.floating (discards size) * int -> np.signed_integer (not np.integer, as is usually meant) --- doc/release/1.14.0-notes.rst | 11 +++++++++++ numpy/core/numerictypes.py | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/doc/release/1.14.0-notes.rst b/doc/release/1.14.0-notes.rst index 5e12b9f4a59a..7a4087dd99a5 100644 --- a/doc/release/1.14.0-notes.rst +++ b/doc/release/1.14.0-notes.rst @@ -25,6 +25,17 @@ Deprecations Future Changes ============== +``np.issubdtype`` will stop downcasting dtype-like arguments +------------------------------------------------------------ +It would be expected that ``issubdtype(np.float32, 'float64')`` and +``issubdtype(np.float32, np.float64)`` mean the same thing - however, there +was an undocumented special case that translated the former into +``issubdtype(np.float32, np.floating)``, giving the surprising result of True. + +This translation now gives a warning explaining what translation is occuring. +In future, the translation will be disabled, and the first example will be made +equivalent to the second. + Build System Changes ==================== diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index fbe89b226fa4..d961d69acbf8 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -85,6 +85,7 @@ import types as _types import sys import numbers +import warnings from numpy.compat import bytes, long from numpy.core.multiarray import ( @@ -763,6 +764,27 @@ def issubdtype(arg1, arg2): mro = arg2.mro() arg2 = mro[1] if len(mro) > 1 else mro[0] + def type_repr(x): + """ Helper to produce clear error messages """ + if not isinstance(x, type): + return repr(x) + elif issubclass(x, generic): + return "np.{}".format(x.__name__) + else: + return x.__name__ + + # 1.14, 2017-08-01 + warnings.warn( + "Conversion of the second argument of issubdtype from `{raw}` " + "to `{abstract}` is deprecated. In future, it will be treated " + "as `{concrete} == np.dtype({raw}).type`.".format( + raw=type_repr(arg2_orig), + abstract=type_repr(arg2), + concrete=type_repr(dtype(arg2_orig).type) + ), + FutureWarning, stacklevel=2 + ) + return issubclass(arg1, arg2)