From 16c0732b435553cbfa6e9d526ba72f288f600a1d Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 04:49:41 -0700 Subject: [PATCH 1/8] Fix lu_factor docstring formatting --- dpnp/linalg/dpnp_iface_linalg.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index 55381c917203..b032780e8db5 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -919,19 +919,22 @@ def lu_factor(a, overwrite_a=False, check_finite=True): a : (M, N) {dpnp.ndarray, usm_ndarray} Input array to decompose. overwrite_a : {None, bool}, optional - Whether to overwrite data in `a` (may increase performance) + Whether to overwrite data in `a` (may increase performance). + Default: ``False``. check_finite : {None, bool}, optional Whether to check that the input matrix contains only finite numbers. Disabling may give a performance gain, but may result in problems (crashes, non-termination) if the inputs do contain infinities or NaNs. + Default: ``True``. + Returns ------- - lu :(M, N) dpnp.ndarray + lu : (M, N) dpnp.ndarray Matrix containing U in its upper triangle, and L in its lower triangle. The unit diagonal elements of L are not stored. - piv (K, ): dpnp.ndarray + piv : (K, ) dpnp.ndarray Pivot indices representing the permutation matrix P: row i of matrix was interchanged with row piv[i]. ``K = min(M, N)``. From f9f9f12c715f40b793e4daf73dae3a418ad72eb4 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 04:51:45 -0700 Subject: [PATCH 2/8] Update warning text for singular matrix --- dpnp/linalg/dpnp_utils_linalg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index 803f8e7326c8..ded935c2d6e6 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -481,7 +481,7 @@ def _batched_lu_factor_scipy(a, res_type): # pylint: disable=too-many-locals if any(dev_info_h): diag_nums = ", ".join(str(v) for v in dev_info_h if v > 0) warn( - f"Diagonal number {diag_nums} are exactly zero. " + f"Diagonal numbers {diag_nums} are exactly zero. " "Singular matrix.", RuntimeWarning, stacklevel=2, @@ -2500,7 +2500,7 @@ def dpnp_lu_factor(a, overwrite_a=False, check_finite=True): if any(dev_info_h): diag_nums = ", ".join(str(v) for v in dev_info_h if v > 0) warn( - f"Diagonal number {diag_nums} are exactly zero. Singular matrix.", + f"Diagonal number {diag_nums} is exactly zero. Singular matrix.", RuntimeWarning, stacklevel=2, ) From 3ba5be97b41f51787ade00904a939627834024c0 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 04:56:50 -0700 Subject: [PATCH 3/8] Prevent race condition in in-place by using submitted_events --- dpnp/linalg/dpnp_utils_linalg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index ded935c2d6e6..5a3320e8251c 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -2493,7 +2493,7 @@ def dpnp_lu_factor(a, overwrite_a=False, check_finite=True): a_h.get_array(), ipiv_h.get_array(), dev_info_h, - depends=[copy_ev] if copy_ev is not None else [], + depends=[copy_ev] if copy_ev is not None else _manager.submitted_events, ) _manager.add_event_pair(ht_ev, getrf_ev) From 12909fd51914e642fe6b067f566ea2d5af433e5c Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 05:16:08 -0700 Subject: [PATCH 4/8] Add dpnp.linalg.lu_factor to generated docs --- doc/reference/linalg.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/reference/linalg.rst b/doc/reference/linalg.rst index 79b85ea81f1c..107b5a86a5b4 100644 --- a/doc/reference/linalg.rst +++ b/doc/reference/linalg.rst @@ -43,6 +43,7 @@ Decompositions dpnp.linalg.cholesky dpnp.linalg.outer dpnp.linalg.qr + dpnp.linalg.lu_factor dpnp.linalg.svd dpnp.linalg.svdvals From 6e7b9f7cb4754f0f21be2adf5e99a37a2542229e Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 05:34:59 -0700 Subject: [PATCH 5/8] Update lu_factor tests by using dpnp.allclose --- dpnp/tests/test_linalg.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py index 2fd001d4d042..ca57ef7da538 100644 --- a/dpnp/tests/test_linalg.py +++ b/dpnp/tests/test_linalg.py @@ -1911,7 +1911,7 @@ def test_lu_factor(self, shape, order, dtype): A_cast = a_dp.astype(LU.dtype, copy=False) PA = self._apply_pivots_rows(A_cast, piv) - assert_allclose(LU, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(LU, PA, rtol=1e-6, atol=1e-6) @pytest.mark.parametrize("dtype", get_float_complex_dtypes()) def test_overwrite_inplace(self, dtype): @@ -1928,7 +1928,7 @@ def test_overwrite_inplace(self, dtype): PA = self._apply_pivots_rows(a_dp_orig, piv) LU = L @ U - assert_allclose(LU, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(LU, PA, rtol=1e-6, atol=1e-6) @pytest.mark.parametrize("dtype", get_float_complex_dtypes()) def test_overwrite_copy(self, dtype): @@ -1945,7 +1945,7 @@ def test_overwrite_copy(self, dtype): PA = self._apply_pivots_rows(a_dp_orig, piv) LU = L @ U - assert_allclose(LU, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(LU, PA, rtol=1e-6, atol=1e-6) def test_overwrite_copy_special(self): # F-contig but dtype != res_type @@ -1972,7 +1972,7 @@ def test_overwrite_copy_special(self): a_orig.astype(L.dtype, copy=False), piv ) LU = L @ U - assert_allclose(LU, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(LU, PA, rtol=1e-6, atol=1e-6) @pytest.mark.parametrize("shape", [(0, 0), (0, 2), (2, 0)]) def test_empty_inputs(self, shape): @@ -2003,7 +2003,7 @@ def test_strided(self, sl): PA = self._apply_pivots_rows(a_dp, piv) LU = L @ U - assert_allclose(LU, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(LU, PA, rtol=1e-6, atol=1e-6) def test_singular_matrix(self): a_dp = dpnp.array([[1.0, 2.0], [2.0, 4.0]]) @@ -2070,7 +2070,7 @@ def test_lu_factor_batched(self, shape, order, dtype): L, U = self._split_lu(lu_3d[i], m, n) A_cast = a_3d[i].astype(L.dtype, copy=False) PA = self._apply_pivots_rows(A_cast, piv_2d[i]) - assert_allclose(L @ U, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(L @ U, PA, rtol=1e-6, atol=1e-6) @pytest.mark.parametrize("dtype", get_float_complex_dtypes()) @pytest.mark.parametrize("order", ["C", "F"]) @@ -2082,7 +2082,7 @@ def test_overwrite(self, dtype, order): ) assert lu is not a_dp - assert_allclose(a_dp, a_dp_orig) + assert dpnp.allclose(a_dp, a_dp_orig) m = n = 2 lu_3d = lu.reshape((-1, m, n)) @@ -2092,7 +2092,7 @@ def test_overwrite(self, dtype, order): L, U = self._split_lu(lu_3d[i], m, n) A_cast = a_3d[i].astype(L.dtype, copy=False) PA = self._apply_pivots_rows(A_cast, piv_2d[i]) - assert_allclose(L @ U, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(L @ U, PA, rtol=1e-6, atol=1e-6) @pytest.mark.parametrize( "shape", [(0, 2, 2), (2, 0, 2), (2, 2, 0), (0, 0, 0)] @@ -2119,7 +2119,7 @@ def test_strided(self): PA = self._apply_pivots_rows( a_stride[i].astype(L.dtype, copy=False), piv[i] ) - assert_allclose(L @ U, PA, rtol=1e-6, atol=1e-6) + assert dpnp.allclose(L @ U, PA, rtol=1e-6, atol=1e-6) def test_singular_matrix(self): a = dpnp.zeros((3, 2, 2), dtype=dpnp.default_float_type()) From 55625eb541a75d6da7791f4fdc2b0bbae4a30418 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 07:06:31 -0700 Subject: [PATCH 6/8] Apply remarks --- dpnp/linalg/dpnp_iface_linalg.py | 17 ++++++++++------- dpnp/linalg/dpnp_utils_linalg.py | 8 ++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index b032780e8db5..5b42db9b2dfc 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -914,9 +914,11 @@ def lu_factor(a, overwrite_a=False, check_finite=True): where `P` is a permutation matrix, `L` is lower triangular with unit diagonal elements, and `U` is upper triangular. + For full documentation refer to :obj:`scipy.linalg.lu_factor`. + Parameters ---------- - a : (M, N) {dpnp.ndarray, usm_ndarray} + a : (..., M, N) {dpnp.ndarray, usm_ndarray} Input array to decompose. overwrite_a : {None, bool}, optional Whether to overwrite data in `a` (may increase performance). @@ -931,13 +933,14 @@ def lu_factor(a, overwrite_a=False, check_finite=True): Returns ------- - lu : (M, N) dpnp.ndarray - Matrix containing U in its upper triangle, and L in its lower triangle. - The unit diagonal elements of L are not stored. - piv : (K, ) dpnp.ndarray - Pivot indices representing the permutation matrix P: + lu : (..., M, N) dpnp.ndarray + Matrix containing `U` in its upper triangle, + and `L` in its lower triangle. + The unit diagonal elements of `L` are not stored. + piv : (..., K) dpnp.ndarray + Pivot indices representing the permutation matrix `P`: row i of matrix was interchanged with row piv[i]. - ``K = min(M, N)``. + Where ``K = min(M, N)``. Warning ------- diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index 5a3320e8251c..1eb0219d9f46 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -2463,17 +2463,17 @@ def dpnp_lu_factor(a, overwrite_a=False, check_finite=True): # - not writeable if not overwrite_a or _is_copy_required(a, res_type): a_h = dpnp.empty_like(a, order="F", dtype=res_type) - ht_ev, copy_ev = ti._copy_usm_ndarray_into_usm_ndarray( + ht_ev, dep_ev = ti._copy_usm_ndarray_into_usm_ndarray( src=a_usm_arr, dst=a_h.get_array(), sycl_queue=a_sycl_queue, depends=_manager.submitted_events, ) - _manager.add_event_pair(ht_ev, copy_ev) + _manager.add_event_pair(ht_ev, dep_ev) else: # input is suitable for in-place modification a_h = a - copy_ev = None + dep_ev = _manager.submitted_events m, n = a.shape @@ -2493,7 +2493,7 @@ def dpnp_lu_factor(a, overwrite_a=False, check_finite=True): a_h.get_array(), ipiv_h.get_array(), dev_info_h, - depends=[copy_ev] if copy_ev is not None else _manager.submitted_events, + depends=dep_ev, ) _manager.add_event_pair(ht_ev, getrf_ev) From 6005cbf9f09ff489dc4eb0d0caa91775cbb41424 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 08:41:02 -0700 Subject: [PATCH 7/8] Remove the limitation section --- dpnp/linalg/dpnp_iface_linalg.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index 5b42db9b2dfc..1a46205ea827 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -947,11 +947,6 @@ def lu_factor(a, overwrite_a=False, check_finite=True): This function synchronizes in order to validate array elements when ``check_finite=True``. - Limitations - ----------- - Only two-dimensional input matrices are supported. - Otherwise, the function raises ``NotImplementedError`` exception. - Examples -------- >>> import dpnp as np From 9714728f7a7cfd02f8b6168054e16510a291c3b6 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Mon, 1 Sep 2025 08:59:56 -0700 Subject: [PATCH 8/8] Pass depends correctly --- dpnp/linalg/dpnp_utils_linalg.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index 1eb0219d9f46..2b8eef552aa9 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -2470,6 +2470,7 @@ def dpnp_lu_factor(a, overwrite_a=False, check_finite=True): depends=_manager.submitted_events, ) _manager.add_event_pair(ht_ev, dep_ev) + dep_ev = [dep_ev] else: # input is suitable for in-place modification a_h = a