From 2418f6e74351c80b0c35d8516cb00c18a5809330 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Fri, 17 Jun 2022 16:22:40 +0200 Subject: [PATCH] MAINT: fix up `_sputils.get_index_dtype` for NEP 50 casting rules The issue boiled down to `np.iinfo(np.int32).max` being a Python int, and comparisons between numpy scalars and Python scalars changing behavior - so avoid using Python scalars. Also fix other sparse issues in the test suite turned up by NEP 50 And makes the tests more concise. Gbp-Pq: Name 0014-MAINT-fix-up-_sputils.get_index_dtype-for-NEP-50-cas.patch --- scipy/sparse/_sputils.py | 5 +- scipy/sparse/linalg/tests/test_matfuncs.py | 6 +- scipy/sparse/tests/test_base.py | 64 ++++++++-------------- scipy/sparse/tests/test_sputils.py | 2 +- 4 files changed, 30 insertions(+), 47 deletions(-) diff --git a/scipy/sparse/_sputils.py b/scipy/sparse/_sputils.py index 6d93b6f9..add718f2 100644 --- a/scipy/sparse/_sputils.py +++ b/scipy/sparse/_sputils.py @@ -150,12 +150,13 @@ def get_index_dtype(arrays=(), maxval=None, check_contents=False): """ - int32min = np.iinfo(np.int32).min - int32max = np.iinfo(np.int32).max + int32min = np.int32(np.iinfo(np.int32).min) + int32max = np.int32(np.iinfo(np.int32).max) # not using intc directly due to misinteractions with pythran dtype = np.int32 if np.intc().itemsize == 4 else np.int64 if maxval is not None: + maxval = np.int64(maxval) if maxval > int32max: dtype = np.int64 diff --git a/scipy/sparse/linalg/tests/test_matfuncs.py b/scipy/sparse/linalg/tests/test_matfuncs.py index 2ea5d11e..8f984553 100644 --- a/scipy/sparse/linalg/tests/test_matfuncs.py +++ b/scipy/sparse/linalg/tests/test_matfuncs.py @@ -113,7 +113,7 @@ class TestExpM: for scale in [1e-2, 1e-1, 5e-1, 1, 10]: A = scale * eye(3, dtype=dtype) observed = expm(A) - expected = exp(scale) * eye(3, dtype=dtype) + expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype) assert_array_almost_equal_nulp(observed, expected, nulp=100) def test_padecases_dtype_complex(self): @@ -121,7 +121,7 @@ class TestExpM: for scale in [1e-2, 1e-1, 5e-1, 1, 10]: A = scale * eye(3, dtype=dtype) observed = expm(A) - expected = exp(scale) * eye(3, dtype=dtype) + expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype) assert_array_almost_equal_nulp(observed, expected, nulp=100) def test_padecases_dtype_sparse_float(self): @@ -129,7 +129,7 @@ class TestExpM: dtype = np.float64 for scale in [1e-2, 1e-1, 5e-1, 1, 10]: a = scale * speye(3, 3, dtype=dtype, format='csc') - e = exp(scale) * eye(3, dtype=dtype) + e = exp(scale, dtype=dtype) * eye(3, dtype=dtype) with suppress_warnings() as sup: sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure of a csc_matrix is expensive.") diff --git a/scipy/sparse/tests/test_base.py b/scipy/sparse/tests/test_base.py index 9e2ec874..c6c95897 100644 --- a/scipy/sparse/tests/test_base.py +++ b/scipy/sparse/tests/test_base.py @@ -97,7 +97,11 @@ def with_64bit_maxval_limit(maxval_limit=None, random=False, fixed_dtype=None, """ if maxval_limit is None: - maxval_limit = 10 + maxval_limit = np.int64(10) + else: + # Ensure we use numpy scalars rather than Python scalars (matters for + # NEP 50 casting rule changes) + maxval_limit = np.int64(maxval_limit) if assert_32bit: def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): @@ -409,21 +413,15 @@ class _TestCommon: assert_array_equal_dtype(dat < dat2, datsp < dat2) assert_array_equal_dtype(datcomplex < dat2, datspcomplex < dat2) # sparse/scalar - assert_array_equal_dtype((datsp < 2).toarray(), dat < 2) - assert_array_equal_dtype((datsp < 1).toarray(), dat < 1) - assert_array_equal_dtype((datsp < 0).toarray(), dat < 0) - assert_array_equal_dtype((datsp < -1).toarray(), dat < -1) - assert_array_equal_dtype((datsp < -2).toarray(), dat < -2) + for val in [2, 1, 0, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp < val).toarray(), dat < val) + assert_array_equal_dtype((val < datsp).toarray(), val < dat) + with np.errstate(invalid='ignore'): assert_array_equal_dtype((datsp < np.nan).toarray(), dat < np.nan) - assert_array_equal_dtype((2 < datsp).toarray(), 2 < dat) - assert_array_equal_dtype((1 < datsp).toarray(), 1 < dat) - assert_array_equal_dtype((0 < datsp).toarray(), 0 < dat) - assert_array_equal_dtype((-1 < datsp).toarray(), -1 < dat) - assert_array_equal_dtype((-2 < datsp).toarray(), -2 < dat) - # data dat = self.dat_dtypes[dtype] datsp = self.datsp_dtypes[dtype] @@ -477,21 +475,15 @@ class _TestCommon: assert_array_equal_dtype(dat > dat2, datsp > dat2) assert_array_equal_dtype(datcomplex > dat2, datspcomplex > dat2) # sparse/scalar - assert_array_equal_dtype((datsp > 2).toarray(), dat > 2) - assert_array_equal_dtype((datsp > 1).toarray(), dat > 1) - assert_array_equal_dtype((datsp > 0).toarray(), dat > 0) - assert_array_equal_dtype((datsp > -1).toarray(), dat > -1) - assert_array_equal_dtype((datsp > -2).toarray(), dat > -2) + for val in [2, 1, 0, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp > val).toarray(), dat > val) + assert_array_equal_dtype((val > datsp).toarray(), val > dat) + with np.errstate(invalid='ignore'): assert_array_equal_dtype((datsp > np.nan).toarray(), dat > np.nan) - assert_array_equal_dtype((2 > datsp).toarray(), 2 > dat) - assert_array_equal_dtype((1 > datsp).toarray(), 1 > dat) - assert_array_equal_dtype((0 > datsp).toarray(), 0 > dat) - assert_array_equal_dtype((-1 > datsp).toarray(), -1 > dat) - assert_array_equal_dtype((-2 > datsp).toarray(), -2 > dat) - # data dat = self.dat_dtypes[dtype] datsp = self.datsp_dtypes[dtype] @@ -545,15 +537,10 @@ class _TestCommon: assert_array_equal_dtype(datsp <= dat2, dat <= dat2) assert_array_equal_dtype(datspcomplex <= dat2, datcomplex <= dat2) # sparse/scalar - assert_array_equal_dtype((datsp <= 2).toarray(), dat <= 2) - assert_array_equal_dtype((datsp <= 1).toarray(), dat <= 1) - assert_array_equal_dtype((datsp <= -1).toarray(), dat <= -1) - assert_array_equal_dtype((datsp <= -2).toarray(), dat <= -2) - - assert_array_equal_dtype((2 <= datsp).toarray(), 2 <= dat) - assert_array_equal_dtype((1 <= datsp).toarray(), 1 <= dat) - assert_array_equal_dtype((-1 <= datsp).toarray(), -1 <= dat) - assert_array_equal_dtype((-2 <= datsp).toarray(), -2 <= dat) + for val in [2, 1, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp <= val).toarray(), dat <= val) + assert_array_equal_dtype((val <= datsp).toarray(), val <= dat) # data dat = self.dat_dtypes[dtype] @@ -608,15 +595,10 @@ class _TestCommon: assert_array_equal_dtype(datsp >= dat2, dat >= dat2) assert_array_equal_dtype(datspcomplex >= dat2, datcomplex >= dat2) # sparse/scalar - assert_array_equal_dtype((datsp >= 2).toarray(), dat >= 2) - assert_array_equal_dtype((datsp >= 1).toarray(), dat >= 1) - assert_array_equal_dtype((datsp >= -1).toarray(), dat >= -1) - assert_array_equal_dtype((datsp >= -2).toarray(), dat >= -2) - - assert_array_equal_dtype((2 >= datsp).toarray(), 2 >= dat) - assert_array_equal_dtype((1 >= datsp).toarray(), 1 >= dat) - assert_array_equal_dtype((-1 >= datsp).toarray(), -1 >= dat) - assert_array_equal_dtype((-2 >= datsp).toarray(), -2 >= dat) + for val in [2, 1, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp >= val).toarray(), dat >= val) + assert_array_equal_dtype((val >= datsp).toarray(), val >= dat) # dense data dat = self.dat_dtypes[dtype] diff --git a/scipy/sparse/tests/test_sputils.py b/scipy/sparse/tests/test_sputils.py index 0eaf573f..fadce9e2 100644 --- a/scipy/sparse/tests/test_sputils.py +++ b/scipy/sparse/tests/test_sputils.py @@ -94,7 +94,7 @@ class TestSparseUtils: sputils.validateaxis(axis) def test_get_index_dtype(self): - imax = np.iinfo(np.int32).max + imax = np.int64(np.iinfo(np.int32).max) too_big = imax + 1 # Check that uint32's with no values too large doesn't return -- 2.30.2