diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ed9491d0ec..eb31d3cd325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Avoided negating unsigned integers in ceil division used in `dpnp.resize` implementation [#2508](https://github.com/IntelPython/dpnp/pull/2508) * Fixed `dpnp.unique` with 1d input array and `axis=0`, `equal_nan=True` keywords passed where the produced result doesn't collapse the NaNs [#2530](https://github.com/IntelPython/dpnp/pull/2530) * Resolved issue when `dpnp.ndarray` constructor is called with `dpnp.ndarray.data` as `buffer` keyword [#2533](https://github.com/IntelPython/dpnp/pull/2533) +* Fixed `dpnp.linalg.cond` to always return a real dtype [#2547](https://github.com/IntelPython/dpnp/pull/2547) ### Security diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index 51cebb2815b..55d140c5c88 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -1976,13 +1976,15 @@ def dpnp_cond(x, p=None): else: r = s[..., 0] / s[..., -1] else: - result_t = _common_type(x) # The result array will contain nans in the entries # where inversion failed invx = dpnp.linalg.inv(x) r = dpnp.linalg.norm(x, p, axis=(-2, -1)) * dpnp.linalg.norm( invx, p, axis=(-2, -1) ) + + # condition number is always real + result_t = _real_type(_common_type(x), device=x.sycl_queue) r = r.astype(result_t, copy=False) # Convert nans to infs unless the original array had nan entries diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py index 931c631b563..34571ddf799 100644 --- a/dpnp/tests/test_linalg.py +++ b/dpnp/tests/test_linalg.py @@ -278,15 +278,12 @@ def test_cholesky_errors(self): class TestCond: - def setup_method(self): - numpy.random.seed(70) + _norms = [None, -dpnp.inf, -2, -1, 1, 2, dpnp.inf, "fro"] @pytest.mark.parametrize( - "shape", [(0, 4, 4), (4, 0, 3, 3)], ids=["(0, 5, 3)", "(4, 0, 2, 3)"] - ) - @pytest.mark.parametrize( - "p", [None, -dpnp.inf, -2, -1, 1, 2, dpnp.inf, "fro"] + "shape", [(0, 4, 4), (4, 0, 3, 3)], ids=["(0, 4, 4)", "(4, 0, 3, 3)"] ) + @pytest.mark.parametrize("p", _norms) def test_empty(self, shape, p): a = numpy.empty(shape) ia = dpnp.array(a) @@ -295,26 +292,27 @@ def test_empty(self, shape, p): expected = numpy.linalg.cond(a, p=p) assert_dtype_allclose(result, expected) + # TODO: uncomment once numpy 2.3.3 release is published + # @testing.with_requires("numpy>=2.3.3") @pytest.mark.parametrize( "dtype", get_all_dtypes(no_none=True, no_bool=True) ) @pytest.mark.parametrize( "shape", [(4, 4), (2, 4, 3, 3)], ids=["(4, 4)", "(2, 4, 3, 3)"] ) - @pytest.mark.parametrize( - "p", [None, -dpnp.inf, -2, -1, 1, 2, dpnp.inf, "fro"] - ) + @pytest.mark.parametrize("p", _norms) def test_basic(self, dtype, shape, p): a = generate_random_numpy_array(shape, dtype) ia = dpnp.array(a) result = dpnp.linalg.cond(ia, p=p) expected = numpy.linalg.cond(a, p=p) + # TODO: remove when numpy#29333 is released + if numpy_version() < "2.3.3": + expected = expected.real assert_dtype_allclose(result, expected, factor=16) - @pytest.mark.parametrize( - "p", [None, -dpnp.inf, -2, -1, 1, 2, dpnp.inf, "fro"] - ) + @pytest.mark.parametrize("p", _norms) def test_bool(self, p): a = numpy.array([[True, True], [True, False]]) ia = dpnp.array(a) @@ -323,9 +321,7 @@ def test_bool(self, p): expected = numpy.linalg.cond(a, p=p) assert_dtype_allclose(result, expected) - @pytest.mark.parametrize( - "p", [None, -dpnp.inf, -2, -1, 1, 2, dpnp.inf, "fro"] - ) + @pytest.mark.parametrize("p", _norms) def test_nan_to_inf(self, p): a = numpy.zeros((2, 2)) ia = dpnp.array(a) @@ -343,9 +339,7 @@ def test_nan_to_inf(self, p): else: assert_raises(dpnp.linalg.LinAlgError, dpnp.linalg.cond, ia, p=p) - @pytest.mark.parametrize( - "p", [None, -dpnp.inf, -2, -1, 1, 2, dpnp.inf, "fro"] - ) + @pytest.mark.parametrize("p", _norms) @pytest.mark.parametrize( "stride", [(-2, -3, 2, -2), (-2, 4, -4, -4), (2, 3, 4, 4), (-1, 3, 3, -3)], @@ -357,21 +351,23 @@ def test_nan_to_inf(self, p): ], ) def test_strided(self, p, stride): - A = numpy.random.rand(6, 8, 10, 10) - B = dpnp.asarray(A) + A = generate_random_numpy_array( + (6, 8, 10, 10), seed_value=70, low=0, high=1 + ) + iA = dpnp.array(A) slices = tuple(slice(None, None, stride[i]) for i in range(A.ndim)) - a = A[slices] - b = B[slices] + a, ia = A[slices], iA[slices] - result = dpnp.linalg.cond(b, p=p) + result = dpnp.linalg.cond(ia, p=p) expected = numpy.linalg.cond(a, p=p) assert_dtype_allclose(result, expected, factor=24) - def test_error(self): + @pytest.mark.parametrize("xp", [dpnp, numpy]) + def test_error(self, xp): # cond is not defined on empty arrays - ia = dpnp.empty((2, 0)) + a = xp.empty((2, 0)) with pytest.raises(ValueError): - dpnp.linalg.cond(ia, p=1) + xp.linalg.cond(a, p=1) class TestDet: