From 36fcb0c209b8493bb10549c85cd22217177860f8 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Fri, 20 Jul 2018 17:45:58 +0800 Subject: [PATCH 01/12] handle empty matrices in qr decomposition --- numpy/linalg/linalg.py | 21 ++++++++++++ numpy/linalg/tests/test_linalg.py | 56 +++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 8e7ad70cdbf8..fbbe69d04472 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -858,6 +858,27 @@ def qr(a, mode='reduced'): a, wrap = _makearray(a) _assertRank2(a) + m, n = a.shape + if _isEmpty2d(a): + k = min(m, n) + if mode == 'reduced': + # ‘reduced’ : returns q, r with dimensions (M, K), (K, N) (default) + return empty((m, k)), empty((k, n)) + elif mode == 'r': + # ‘r’ : returns r only with dimensions (K, N) + return empty((k, n)) + elif mode == 'complete': + # ‘complete’ : returns q, r with dimensions (M, M), (M, N) + return eye(m), empty((m, n)) + elif mode in ('raw', 'economic'): + # ‘raw’ : returns h, tau with dimensions (N, M), (K,) + h = empty((n, m)) + tau = empty(k) + if mode == 'economic': + # ‘economic’ : returns h from ‘raw’, deprecated. + return h + else: + return h, tau _assertNoEmpty2d(a) m, n = a.shape t, result_t = _commonType(a) diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 1c24f1e041f0..7a84160b69ce 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1583,8 +1583,51 @@ def check_qr(self, a): assert_almost_equal(r2, r1) def test_qr_empty(self): - a = np.zeros((0, 2)) - assert_raises(linalg.LinAlgError, linalg.qr, a) + for m, n in [(3, 0), (0, 3)]: + k = min(m, n) + a = np.empty((m, n)) + a_type = type(a) + a_dtype = a.dtype + # + q, r = np.linalg.qr(a, mode='reduced') + assert_(q.dtype == a_dtype) + assert_(r.dtype == a_dtype) + assert_(isinstance(q, a_type)) + assert_(isinstance(r, a_type)) + assert_(q.shape == (m, k)) + assert_(r.shape == (k, n)) + assert_almost_equal(np.dot(q, r), a) + assert_almost_equal(np.dot(q.T.conj(), q), np.eye(k)) + assert_almost_equal(np.triu(r), r) + # + r = np.linalg.qr(a, mode='r') + assert_(r.dtype == a_dtype) + assert_(isinstance(r, a_type)) + assert_(r.shape == (k, n)) + assert_almost_equal(np.triu(r), r) + # + q, r = np.linalg.qr(a, mode='complete') + assert_(q.dtype == a_dtype) + assert_(r.dtype == a_dtype) + assert_(isinstance(q, a_type)) + assert_(isinstance(r, a_type)) + assert_(q.shape == (m, m)) + assert_(r.shape == (m, n)) + assert_almost_equal(q, np.eye(m)) + assert_almost_equal(np.dot(q, r), a) + assert_almost_equal(np.triu(r), r) + # + h, tau = np.linalg.qr(a, mode='raw') + assert_(h.dtype == np.double) + assert_(tau.dtype == np.double) + assert_(h.shape == (n, m)) + assert_(tau.shape == (k,)) + # + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, "The 'economic' option is deprecated.") + h = np.linalg.qr(a, mode='economic') + assert_(h.dtype == np.double) + assert_(h.shape == (n, m)) def test_mode_raw(self): # The factorization is not unique and varies between libraries, @@ -1625,15 +1668,6 @@ def test_mode_all_but_economic(self): self.check_qr(m2) self.check_qr(m2.T) - def test_0_size(self): - # There may be good ways to do (some of this) reasonably: - a = np.zeros((0, 0)) - assert_raises(linalg.LinAlgError, linalg.qr, a) - a = np.zeros((0, 1)) - assert_raises(linalg.LinAlgError, linalg.qr, a) - a = np.zeros((1, 0)) - assert_raises(linalg.LinAlgError, linalg.qr, a) - class TestCholesky(object): # TODO: are there no other tests for cholesky? From 3fa693ae72cc312f2f3601602fb0bacc681ed1ce Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Fri, 20 Jul 2018 22:37:02 +0800 Subject: [PATCH 02/12] fixed offending quotes --- numpy/linalg/linalg.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index fbbe69d04472..0888d13cb545 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -862,20 +862,20 @@ def qr(a, mode='reduced'): if _isEmpty2d(a): k = min(m, n) if mode == 'reduced': - # ‘reduced’ : returns q, r with dimensions (M, K), (K, N) (default) + # 'reduced': returns q, r with dimensions (M, K), (K, N) (default) return empty((m, k)), empty((k, n)) elif mode == 'r': - # ‘r’ : returns r only with dimensions (K, N) + # 'r': returns r only with dimensions (K, N) return empty((k, n)) elif mode == 'complete': - # ‘complete’ : returns q, r with dimensions (M, M), (M, N) + # 'complete': returns q, r with dimensions (M, M), (M, N) return eye(m), empty((m, n)) elif mode in ('raw', 'economic'): - # ‘raw’ : returns h, tau with dimensions (N, M), (K,) + # 'raw': returns h, tau with dimensions (N, M), (K,) h = empty((n, m)) tau = empty(k) if mode == 'economic': - # ‘economic’ : returns h from ‘raw’, deprecated. + # 'economic': returns h from raw, deprecated. return h else: return h, tau From 6dabc3937196efcd15d79e28e7e691dca3804ffe Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Sat, 21 Jul 2018 00:16:29 +0800 Subject: [PATCH 03/12] release notes updated --- doc/release/1.16.0-notes.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/release/1.16.0-notes.rst b/doc/release/1.16.0-notes.rst index ae21f4ffdf42..e0ea571abc74 100644 --- a/doc/release/1.16.0-notes.rst +++ b/doc/release/1.16.0-notes.rst @@ -47,6 +47,12 @@ Even when no elements needed to be drawn, ``np.random.randint`` and distribution. This has been fixed so that e.g. ``np.random.choice([], 0) == np.array([], dtype=float64)``. +``linalg.qr`` now works with empty matrices +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, a ``LinAlgError`` would be raised when empty matrix ("flat" +or "skinny") is passed in. This has been fixed so that outputs of +appropriate shapes are returned for the various modes. + ARM support updated ------------------- Support for ARM CPUs has been updated to accommodate 32 and 64 bit targets, From 448b4099028961641b1e02834ae6da49195bd186 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Sun, 29 Jul 2018 14:35:13 +0800 Subject: [PATCH 04/12] revised to use existing array sizes; skipping calls to lapack in the case of empty arrays (they do not play well with empty arrays) --- numpy/linalg/linalg.py | 110 +++++++++++++----------------- numpy/linalg/tests/test_linalg.py | 6 -- 2 files changed, 46 insertions(+), 70 deletions(-) diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 0888d13cb545..4753bafa0c2b 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -859,53 +859,34 @@ def qr(a, mode='reduced'): a, wrap = _makearray(a) _assertRank2(a) m, n = a.shape - if _isEmpty2d(a): - k = min(m, n) - if mode == 'reduced': - # 'reduced': returns q, r with dimensions (M, K), (K, N) (default) - return empty((m, k)), empty((k, n)) - elif mode == 'r': - # 'r': returns r only with dimensions (K, N) - return empty((k, n)) - elif mode == 'complete': - # 'complete': returns q, r with dimensions (M, M), (M, N) - return eye(m), empty((m, n)) - elif mode in ('raw', 'economic'): - # 'raw': returns h, tau with dimensions (N, M), (K,) - h = empty((n, m)) - tau = empty(k) - if mode == 'economic': - # 'economic': returns h from raw, deprecated. - return h - else: - return h, tau - _assertNoEmpty2d(a) - m, n = a.shape + is_non_empty = not _isEmpty2d(a) t, result_t = _commonType(a) a = _fastCopyAndTranspose(t, a) a = _to_native_byte_order(a) mn = min(m, n) tau = zeros((mn,), t) - if isComplexType(t): - lapack_routine = lapack_lite.zgeqrf - routine_name = 'zgeqrf' - else: - lapack_routine = lapack_lite.dgeqrf - routine_name = 'dgeqrf' - - # calculate optimal size of work data 'work' - lwork = 1 - work = zeros((lwork,), t) - results = lapack_routine(m, n, a, m, tau, work, -1, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) - - # do qr decomposition - lwork = int(abs(work[0])) - work = zeros((lwork,), t) - results = lapack_routine(m, n, a, m, tau, work, lwork, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + + if is_non_empty: + if isComplexType(t): + lapack_routine = lapack_lite.zgeqrf + routine_name = 'zgeqrf' + else: + lapack_routine = lapack_lite.dgeqrf + routine_name = 'dgeqrf' + + # calculate optimal size of work data 'work' + lwork = 1 + work = zeros((lwork,), t) + results = lapack_routine(m, n, a, m, tau, work, -1, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + + # do qr decomposition + lwork = int(abs(work[0])) + work = zeros((lwork,), t) + results = lapack_routine(m, n, a, m, tau, work, lwork, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) # handle modes that don't return q if mode == 'r': @@ -923,32 +904,33 @@ def qr(a, mode='reduced'): # generate q from a if mode == 'complete' and m > n: mc = m - q = empty((m, m), t) + q = empty((m, m), t) if is_non_empty else eye(m, dtype=t) else: mc = mn - q = empty((n, m), t) + q = empty((n, m), t) if is_non_empty else eye(n, m, dtype=t) q[:n] = a - if isComplexType(t): - lapack_routine = lapack_lite.zungqr - routine_name = 'zungqr' - else: - lapack_routine = lapack_lite.dorgqr - routine_name = 'dorgqr' - - # determine optimal lwork - lwork = 1 - work = zeros((lwork,), t) - results = lapack_routine(m, mc, mn, q, m, tau, work, -1, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) - - # compute q - lwork = int(abs(work[0])) - work = zeros((lwork,), t) - results = lapack_routine(m, mc, mn, q, m, tau, work, lwork, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + if is_non_empty: + if isComplexType(t): + lapack_routine = lapack_lite.zungqr + routine_name = 'zungqr' + else: + lapack_routine = lapack_lite.dorgqr + routine_name = 'dorgqr' + + # determine optimal lwork + lwork = 1 + work = zeros((lwork,), t) + results = lapack_routine(m, mc, mn, q, m, tau, work, -1, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + + # compute q + lwork = int(abs(work[0])) + work = zeros((lwork,), t) + results = lapack_routine(m, mc, mn, q, m, tau, work, lwork, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) q = _fastCopyAndTranspose(result_t, q[:mc]) r = _fastCopyAndTranspose(result_t, a[:, :mc]) diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 7a84160b69ce..4eae6da5b948 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1622,12 +1622,6 @@ def test_qr_empty(self): assert_(tau.dtype == np.double) assert_(h.shape == (n, m)) assert_(tau.shape == (k,)) - # - with suppress_warnings() as sup: - sup.filter(DeprecationWarning, "The 'economic' option is deprecated.") - h = np.linalg.qr(a, mode='economic') - assert_(h.dtype == np.double) - assert_(h.shape == (n, m)) def test_mode_raw(self): # The factorization is not unique and varies between libraries, From 1624a805e26b02731185ca6deb0a0e019b909e59 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Mon, 30 Jul 2018 01:39:27 +0800 Subject: [PATCH 05/12] setting things back where they were and calling LAPACK functions better --- numpy/linalg/linalg.py | 87 ++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 4753bafa0c2b..037fcb9d3b76 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -859,34 +859,32 @@ def qr(a, mode='reduced'): a, wrap = _makearray(a) _assertRank2(a) m, n = a.shape - is_non_empty = not _isEmpty2d(a) t, result_t = _commonType(a) a = _fastCopyAndTranspose(t, a) a = _to_native_byte_order(a) mn = min(m, n) tau = zeros((mn,), t) - if is_non_empty: - if isComplexType(t): - lapack_routine = lapack_lite.zgeqrf - routine_name = 'zgeqrf' - else: - lapack_routine = lapack_lite.dgeqrf - routine_name = 'dgeqrf' - - # calculate optimal size of work data 'work' - lwork = 1 - work = zeros((lwork,), t) - results = lapack_routine(m, n, a, m, tau, work, -1, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) - - # do qr decomposition - lwork = int(abs(work[0])) - work = zeros((lwork,), t) - results = lapack_routine(m, n, a, m, tau, work, lwork, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + if isComplexType(t): + lapack_routine = lapack_lite.zgeqrf + routine_name = 'zgeqrf' + else: + lapack_routine = lapack_lite.dgeqrf + routine_name = 'dgeqrf' + + # calculate optimal size of work data 'work' + lwork = 1 + work = zeros((lwork,), t) + results = lapack_routine(m, n, a, max(1, m), tau, work, -1, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + + # do qr decomposition + lwork = max(1, int(abs(work[0]))) + work = zeros((lwork,), t) + results = lapack_routine(m, n, a, max(1, m), tau, work, lwork, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) # handle modes that don't return q if mode == 'r': @@ -904,33 +902,32 @@ def qr(a, mode='reduced'): # generate q from a if mode == 'complete' and m > n: mc = m - q = empty((m, m), t) if is_non_empty else eye(m, dtype=t) + q = empty((m, m), t) else: mc = mn - q = empty((n, m), t) if is_non_empty else eye(n, m, dtype=t) + q = empty((n, m), t) q[:n] = a - if is_non_empty: - if isComplexType(t): - lapack_routine = lapack_lite.zungqr - routine_name = 'zungqr' - else: - lapack_routine = lapack_lite.dorgqr - routine_name = 'dorgqr' - - # determine optimal lwork - lwork = 1 - work = zeros((lwork,), t) - results = lapack_routine(m, mc, mn, q, m, tau, work, -1, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) - - # compute q - lwork = int(abs(work[0])) - work = zeros((lwork,), t) - results = lapack_routine(m, mc, mn, q, m, tau, work, lwork, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + if isComplexType(t): + lapack_routine = lapack_lite.zungqr + routine_name = 'zungqr' + else: + lapack_routine = lapack_lite.dorgqr + routine_name = 'dorgqr' + + # determine optimal lwork + lwork = 1 + work = zeros((lwork,), t) + results = lapack_routine(m, mc, mn, q, max(1, m), tau, work, -1, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + + # compute q + lwork = max(1, int(abs(work[0]))) + work = zeros((lwork,), t) + results = lapack_routine(m, mc, mn, q, max(1, m), tau, work, lwork, 0) + if results['info'] != 0: + raise LinAlgError('%s returns %d' % (routine_name, results['info'])) q = _fastCopyAndTranspose(result_t, q[:mc]) r = _fastCopyAndTranspose(result_t, a[:, :mc]) From e6fa7df6e579de2d9c9bfcbc97ccecc3d27936ef Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Mon, 30 Jul 2018 02:18:44 +0800 Subject: [PATCH 06/12] ensure lwork respects requirements of lapack methods (zgeqrf, dgeqrf, zungqr, dorgqr) --- numpy/linalg/linalg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 037fcb9d3b76..a7217ccbacc3 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -880,7 +880,7 @@ def qr(a, mode='reduced'): raise LinAlgError('%s returns %d' % (routine_name, results['info'])) # do qr decomposition - lwork = max(1, int(abs(work[0]))) + lwork = max(1, n, int(abs(work[0]))) work = zeros((lwork,), t) results = lapack_routine(m, n, a, max(1, m), tau, work, lwork, 0) if results['info'] != 0: @@ -923,7 +923,7 @@ def qr(a, mode='reduced'): raise LinAlgError('%s returns %d' % (routine_name, results['info'])) # compute q - lwork = max(1, int(abs(work[0]))) + lwork = max(1, n, int(abs(work[0]))) work = zeros((lwork,), t) results = lapack_routine(m, mc, mn, q, max(1, m), tau, work, lwork, 0) if results['info'] != 0: From 7f2273674ed8b0082c6da12d15ce23151a1056f2 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Mon, 30 Jul 2018 02:54:29 +0800 Subject: [PATCH 07/12] updated tests --- numpy/linalg/tests/test_linalg.py | 44 +++++++++++++------------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 4eae6da5b948..d73a2032e85d 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1583,45 +1583,39 @@ def check_qr(self, a): assert_almost_equal(r2, r1) def test_qr_empty(self): - for m, n in [(3, 0), (0, 3)]: + for m, n in [(0, 0), (3, 0), (0, 3)]: k = min(m, n) a = np.empty((m, n)) a_type = type(a) a_dtype = a.dtype - # + q, r = np.linalg.qr(a, mode='reduced') - assert_(q.dtype == a_dtype) - assert_(r.dtype == a_dtype) + assert_equal(q.dtype, a_dtype) + assert_equal(r.dtype, a_dtype) assert_(isinstance(q, a_type)) assert_(isinstance(r, a_type)) - assert_(q.shape == (m, k)) - assert_(r.shape == (k, n)) - assert_almost_equal(np.dot(q, r), a) - assert_almost_equal(np.dot(q.T.conj(), q), np.eye(k)) - assert_almost_equal(np.triu(r), r) - # + assert_equal(q.shape, (m, k)) + assert_equal(r.shape, (k, n)) + r = np.linalg.qr(a, mode='r') - assert_(r.dtype == a_dtype) + assert_equal(r.dtype, a_dtype) assert_(isinstance(r, a_type)) - assert_(r.shape == (k, n)) - assert_almost_equal(np.triu(r), r) - # + assert_equal(r.shape, (k, n)) + q, r = np.linalg.qr(a, mode='complete') - assert_(q.dtype == a_dtype) - assert_(r.dtype == a_dtype) + assert_equal(q.dtype, a_dtype) + assert_equal(r.dtype, a_dtype) assert_(isinstance(q, a_type)) assert_(isinstance(r, a_type)) - assert_(q.shape == (m, m)) - assert_(r.shape == (m, n)) + assert_equal(q.shape, (m, m)) + assert_equal(r.shape, (m, n)) assert_almost_equal(q, np.eye(m)) - assert_almost_equal(np.dot(q, r), a) - assert_almost_equal(np.triu(r), r) - # + h, tau = np.linalg.qr(a, mode='raw') - assert_(h.dtype == np.double) - assert_(tau.dtype == np.double) - assert_(h.shape == (n, m)) - assert_(tau.shape == (k,)) + assert_equal(h.dtype, np.double) + assert_equal(tau.dtype, np.double) + assert_equal(h.shape, (n, m)) + assert_equal(tau.shape, (k,)) def test_mode_raw(self): # The factorization is not unique and varies between libraries, From 68d608e2b28fae0704995ec016b2c3e9fd536f74 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Mon, 30 Jul 2018 03:15:55 +0800 Subject: [PATCH 08/12] simplified tests --- numpy/linalg/tests/test_linalg.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index d73a2032e85d..1f58d367e862 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1589,28 +1589,13 @@ def test_qr_empty(self): a_type = type(a) a_dtype = a.dtype - q, r = np.linalg.qr(a, mode='reduced') - assert_equal(q.dtype, a_dtype) - assert_equal(r.dtype, a_dtype) - assert_(isinstance(q, a_type)) - assert_(isinstance(r, a_type)) - assert_equal(q.shape, (m, k)) - assert_equal(r.shape, (k, n)) + self.check_qr(a) r = np.linalg.qr(a, mode='r') assert_equal(r.dtype, a_dtype) assert_(isinstance(r, a_type)) assert_equal(r.shape, (k, n)) - q, r = np.linalg.qr(a, mode='complete') - assert_equal(q.dtype, a_dtype) - assert_equal(r.dtype, a_dtype) - assert_(isinstance(q, a_type)) - assert_(isinstance(r, a_type)) - assert_equal(q.shape, (m, m)) - assert_equal(r.shape, (m, n)) - assert_almost_equal(q, np.eye(m)) - h, tau = np.linalg.qr(a, mode='raw') assert_equal(h.dtype, np.double) assert_equal(tau.dtype, np.double) From 5809e9aade20b2dc69b03d90b7546a57463d3c6a Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Tue, 31 Jul 2018 14:52:33 +0800 Subject: [PATCH 09/12] removed redundant test; expressed sub-test with pytest.mark.parametrize --- numpy/linalg/tests/test_linalg.py | 41 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 1f58d367e862..cfd8250f3d2e 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1582,25 +1582,28 @@ def check_qr(self, a): assert_(isinstance(r2, a_type)) assert_almost_equal(r2, r1) - def test_qr_empty(self): - for m, n in [(0, 0), (3, 0), (0, 3)]: - k = min(m, n) - a = np.empty((m, n)) - a_type = type(a) - a_dtype = a.dtype - - self.check_qr(a) - - r = np.linalg.qr(a, mode='r') - assert_equal(r.dtype, a_dtype) - assert_(isinstance(r, a_type)) - assert_equal(r.shape, (k, n)) - - h, tau = np.linalg.qr(a, mode='raw') - assert_equal(h.dtype, np.double) - assert_equal(tau.dtype, np.double) - assert_equal(h.shape, (n, m)) - assert_equal(tau.shape, (k,)) + + @pytest.mark.parametrize(["m", "n"], [ + (0, 4, 1), + (0, 4, 2), + (4, 0, 1), + (4, 0, 2), + (4, 2, 0), + (0, 0, 0) + ]) + def test_qr_empty(self, m, n): + k = min(m, n) + a = np.empty((m, n)) + a_type = type(a) + a_dtype = a.dtype + + self.check_qr(a) + + h, tau = np.linalg.qr(a, mode='raw') + assert_equal(h.dtype, np.double) + assert_equal(tau.dtype, np.double) + assert_equal(h.shape, (n, m)) + assert_equal(tau.shape, (k,)) def test_mode_raw(self): # The factorization is not unique and varies between libraries, From 16d79e9674e536d216beee56af75a1f3093b0ea4 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Tue, 31 Jul 2018 15:04:28 +0800 Subject: [PATCH 10/12] fixed dumb copy-and-paste error --- numpy/linalg/tests/test_linalg.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index cfd8250f3d2e..0df673884f6f 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1584,12 +1584,9 @@ def check_qr(self, a): @pytest.mark.parametrize(["m", "n"], [ - (0, 4, 1), - (0, 4, 2), - (4, 0, 1), - (4, 0, 2), - (4, 2, 0), - (0, 0, 0) + (3, 0), + (0, 3), + (0, 0) ]) def test_qr_empty(self, m, n): k = min(m, n) From c4c64268702a72d7ca9ce2a620a04a303fbacce8 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Tue, 31 Jul 2018 15:18:15 +0800 Subject: [PATCH 11/12] clarified terminology --- doc/release/1.16.0-notes.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/release/1.16.0-notes.rst b/doc/release/1.16.0-notes.rst index e0ea571abc74..9c5f8a067529 100644 --- a/doc/release/1.16.0-notes.rst +++ b/doc/release/1.16.0-notes.rst @@ -49,9 +49,9 @@ distribution. This has been fixed so that e.g. ``linalg.qr`` now works with empty matrices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Previously, a ``LinAlgError`` would be raised when empty matrix ("flat" -or "skinny") is passed in. This has been fixed so that outputs of -appropriate shapes are returned for the various modes. +Previously, a ``LinAlgError`` would be raised when empty matrix +(with zero rows and/or columns) is passed in. This has been fixed +so that outputs of appropriate shapes are returned for the various modes. ARM support updated ------------------- From ff9063c9f2572205164f42e72f307809be297986 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Tue, 31 Jul 2018 15:20:38 +0800 Subject: [PATCH 12/12] trimmed excess on line --- doc/release/1.16.0-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release/1.16.0-notes.rst b/doc/release/1.16.0-notes.rst index 9c5f8a067529..000c69cef074 100644 --- a/doc/release/1.16.0-notes.rst +++ b/doc/release/1.16.0-notes.rst @@ -48,7 +48,7 @@ distribution. This has been fixed so that e.g. ``np.random.choice([], 0) == np.array([], dtype=float64)``. ``linalg.qr`` now works with empty matrices -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Previously, a ``LinAlgError`` would be raised when empty matrix (with zero rows and/or columns) is passed in. This has been fixed so that outputs of appropriate shapes are returned for the various modes.