Skip to content

Commit 03e5ba0

Browse files
authored
Add third party tests for dpnp.linalg.cond (#2482)
This PR adds third party tests for `dpnp.linalg.cond` function. Also it adds more clarity in documentation on possible values of `p` keyword.
1 parent aec9934 commit 03e5ba0

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

dpnp/linalg/dpnp_iface_linalg.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,22 @@ def cond(x, p=None):
180180
x : {dpnp.ndarray, usm_ndarray}
181181
The matrix whose condition number is sought.
182182
p : {None, 1, -1, 2, -2, inf, -inf, "fro"}, optional
183-
Order of the norm used in the condition number computation.
184-
``inf`` means the `dpnp.inf` object, and the Frobenius norm is
183+
Order of the norm used in the condition number computation:
184+
185+
===== ============================
186+
p norm for matrices
187+
===== ============================
188+
None 2-norm
189+
'fro' Frobenius norm
190+
inf max(sum(abs(x), axis=1))
191+
-inf min(sum(abs(x), axis=1))
192+
1 max(sum(abs(x), axis=0))
193+
-1 min(sum(abs(x), axis=0))
194+
2 2-norm (largest singular value)
195+
-2 smallest singular value
196+
===== ============================
197+
198+
``inf`` means the :obj:`dpnp.inf` object, and the Frobenius norm is
185199
the root-of-sum-of-squares norm.
186200
187201
Default: ``None``.

dpnp/tests/third_party/cupy/linalg_tests/test_norms.py

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pytest
55

66
import dpnp as cupy
7-
from dpnp.tests.helper import is_cpu_device
87
from dpnp.tests.third_party.cupy import testing
98

109

@@ -224,3 +223,133 @@ def test_slogdet_one_dim(self, dtype):
224223
a = testing.shaped_arange((2,), xp, dtype)
225224
with pytest.raises(xp.linalg.LinAlgError):
226225
xp.linalg.slogdet(a)
226+
227+
228+
@testing.parameterize(
229+
*testing.product({"ord": [-numpy.inf, -2, -1, 1, 2, numpy.inf, "fro"]})
230+
)
231+
class TestCond(unittest.TestCase):
232+
@testing.for_float_dtypes(no_float16=True)
233+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
234+
def test_singular_zeros(self, xp, dtype):
235+
if self.ord not in [None, 2, -2]:
236+
pytest.skip("no LinAlgError is raising on singular matrices")
237+
238+
A = xp.zeros(shape=(2, 2), dtype=dtype)
239+
result = xp.linalg.cond(A, self.ord)
240+
241+
# singular matrices don't always hit infinity.
242+
result = xp.asarray(result) # numpy is scalar and can't be replaced
243+
large_number = 1.0 / (xp.finfo(dtype).eps)
244+
result[result >= large_number] = xp.inf
245+
246+
return result
247+
248+
@testing.for_float_dtypes(no_float16=True)
249+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
250+
def test_singular_ones(self, xp, dtype):
251+
if self.ord not in [None, 2, -2]:
252+
pytest.skip("no LinAlgError is raising on singular matrices")
253+
254+
A = xp.ones(shape=(2, 2), dtype=dtype)
255+
result = xp.linalg.cond(A, self.ord)
256+
257+
# singular matrices don't always hit infinity.
258+
result = xp.asarray(result) # numpy is scalar and can't be replaced
259+
large_number = 1.0 / (xp.finfo(dtype).eps)
260+
result[result >= large_number] = xp.inf
261+
262+
return result
263+
264+
@testing.for_float_dtypes(no_float16=True)
265+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
266+
def test_stacked_singular(self, xp, dtype):
267+
if self.ord not in [None, 2, -2]:
268+
pytest.skip("no LinAlgError is raising on singular matrices")
269+
270+
# Check behavior when only some of the stacked matrices are
271+
# singular
272+
273+
A = xp.arange(16, dtype=dtype).reshape((2, 2, 2, 2))
274+
A[0, 0] = 0
275+
A[1, 1] = 0
276+
277+
res = xp.linalg.cond(A, self.ord)
278+
return res
279+
280+
@testing.for_float_dtypes(no_float16=True)
281+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
282+
def test_default(self, xp, dtype):
283+
A = testing.shaped_arange((2, 2), xp, dtype=dtype)
284+
return xp.linalg.cond(A)
285+
286+
@testing.for_float_dtypes(no_float16=True)
287+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
288+
def test_basic(self, xp, dtype):
289+
A = testing.shaped_arange((2, 2), xp, dtype=dtype)
290+
return xp.linalg.cond(A, self.ord)
291+
292+
@testing.for_float_dtypes(no_float16=True)
293+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
294+
def test_generalized_1(self, xp, dtype):
295+
A = testing.shaped_arange((2, 2), xp, dtype=dtype)
296+
A = xp.array([A, 2 * A, 3 * A])
297+
return xp.linalg.cond(A, self.ord)
298+
299+
@testing.for_float_dtypes(no_float16=True)
300+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
301+
def test_generalized_2(self, xp, dtype):
302+
A = testing.shaped_arange((2, 2), xp, dtype=dtype)
303+
A = xp.array([A, 2 * A, 3 * A])
304+
A = xp.array([A] * 2 * 3).reshape((3, 2) + A.shape)
305+
306+
return xp.linalg.cond(A, self.ord)
307+
308+
@testing.for_float_dtypes(no_float16=True)
309+
def test_0x0(self, dtype):
310+
for xp in (numpy, cupy):
311+
A = xp.empty((0, 0), dtype=dtype)
312+
with pytest.raises(
313+
xp.linalg.LinAlgError,
314+
match="cond is not defined on empty arrays",
315+
):
316+
xp.linalg.cond(A, self.ord)
317+
318+
@testing.for_float_dtypes(no_float16=True)
319+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
320+
def test_1x1(self, xp, dtype):
321+
A = xp.ones((1, 1), dtype=dtype)
322+
return xp.linalg.cond(A, self.ord)
323+
324+
@testing.for_float_dtypes(no_float16=True)
325+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
326+
def test_8x8(self, xp, dtype):
327+
A = testing.shaped_arange((8, 8), xp, dtype=dtype) + xp.diag(
328+
xp.ones(8, dtype=dtype)
329+
)
330+
return xp.linalg.cond(A, self.ord)
331+
332+
@pytest.mark.skip("only ndarray input is supported")
333+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
334+
def test_nonarray(self, xp):
335+
A = [[1.0, 2.0], [3.0, 4.0]]
336+
return xp.linalg.cond(A, self.ord)
337+
338+
@testing.for_float_dtypes(no_float16=True)
339+
@testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4)
340+
def test_hermitian(self, xp, dtype):
341+
A = xp.array([[1.0, 2.0], [2.0, 1.0]], dtype=dtype)
342+
return xp.linalg.cond(A, self.ord)
343+
344+
345+
class TestCondBasicNonSVD(unittest.TestCase):
346+
def test_basic_nonsvd(self):
347+
# Smoketest the non-svd norms
348+
A = cupy.array([[1.0, 0, 1], [0, -2.0, 0], [0, 0, 3.0]])
349+
testing.assert_array_almost_equal(cupy.linalg.cond(A, cupy.inf), 4)
350+
testing.assert_array_almost_equal(cupy.linalg.cond(A, -cupy.inf), 2 / 3)
351+
testing.assert_array_almost_equal(cupy.linalg.cond(A, 1), 4)
352+
testing.assert_array_almost_equal(cupy.linalg.cond(A, -1), 0.5)
353+
testing.assert_array_almost_equal(
354+
cupy.linalg.cond(A, "fro"), numpy.sqrt(265 / 12)
355+
)

0 commit comments

Comments
 (0)