Skip to content

Commit 50b177d

Browse files
vtavanaantonwolfy
andauthored
update dpnp.cond (#1773)
* update dpnp.cond * address comments * update pinv function * Update dpnp/linalg/dpnp_utils_linalg.py --------- Co-authored-by: Anton <100830759+antonwolfy@users.noreply.github.com>
1 parent bea4c9c commit 50b177d

10 files changed

Lines changed: 278 additions & 114 deletions

File tree

dpnp/linalg/dpnp_algo_linalg.pyx

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ cimport numpy
4545
cimport dpnp.dpnp_utils as utils
4646

4747
__all__ = [
48-
"dpnp_cond",
4948
"dpnp_eig",
5049
"dpnp_eigvals",
5150
]
@@ -60,30 +59,6 @@ ctypedef c_dpctl.DPCTLSyclEventRef(*custom_linalg_2in_1out_func_ptr_t)(c_dpctl.D
6059
const c_dpctl.DPCTLEventVectorRef)
6160

6261

63-
cpdef object dpnp_cond(object input, object p):
64-
if p in ('f', 'fro'):
65-
# TODO: change order='K' when support is implemented
66-
input = dpnp.ravel(input, order='C')
67-
sqnorm = dpnp.dot(input, input)
68-
res = dpnp.sqrt(sqnorm)
69-
ret = dpnp.array([res])
70-
elif p == dpnp.inf:
71-
dpnp_sum_val = dpnp.sum(dpnp.abs(input), axis=1)
72-
ret = dpnp.max(dpnp_sum_val)
73-
elif p == -dpnp.inf:
74-
dpnp_sum_val = dpnp.sum(dpnp.abs(input), axis=1)
75-
ret = dpnp.min(dpnp_sum_val)
76-
elif p == 1:
77-
dpnp_sum_val = dpnp.sum(dpnp.abs(input), axis=0)
78-
ret = dpnp.max(dpnp_sum_val)
79-
elif p == -1:
80-
dpnp_sum_val = dpnp.sum(dpnp.abs(input), axis=0)
81-
ret = dpnp.min(dpnp_sum_val)
82-
else:
83-
ret = dpnp.array([input.item(0)])
84-
return ret
85-
86-
8762
cpdef tuple dpnp_eig(utils.dpnp_descriptor x1):
8863
cdef shape_type_c x1_shape = x1.shape
8964

dpnp/linalg/dpnp_iface_linalg.py

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
check_stacked_2d,
4949
check_stacked_square,
5050
dpnp_cholesky,
51+
dpnp_cond,
5152
dpnp_det,
5253
dpnp_eigh,
5354
dpnp_inv,
@@ -145,32 +146,61 @@ def cholesky(a, upper=False):
145146
return dpnp_cholesky(a, upper=upper)
146147

147148

148-
def cond(input, p=None):
149+
def cond(x, p=None):
149150
"""
150151
Compute the condition number of a matrix.
151152
152153
For full documentation refer to :obj:`numpy.linalg.cond`.
153154
154-
Limitations
155-
-----------
156-
Input array is supported as :obj:`dpnp.ndarray`.
157-
Parameter p=[None, 1, -1, 2, -2, dpnp.inf, -dpnp.inf, 'fro'] is supported.
155+
Parameters
156+
----------
157+
x : {dpnp.ndarray, usm_ndarray}
158+
The matrix whose condition number is sought.
159+
p : {None, 1, -1, 2, -2, inf, -inf, "fro"}, optional
160+
Order of the norm used in the condition number computation.
161+
inf means the `dpnp.inf` object, and the Frobenius norm is
162+
the root-of-sum-of-squares norm. The default is ``None``.
163+
164+
Returns
165+
-------
166+
out : dpnp.ndarray
167+
The condition number of the matrix. May be infinite.
158168
159169
See Also
160170
--------
161-
:obj:`dpnp.norm` : Matrix or vector norm.
162-
"""
171+
:obj:`dpnp.linalg.norm` : Matrix or vector norm.
163172
164-
if not use_origin_backend(input):
165-
if p in [None, 1, -1, 2, -2, dpnp.inf, -dpnp.inf, "fro"]:
166-
result_obj = dpnp_cond(input, p)
167-
result = dpnp.convert_single_elem_array_to_scalar(result_obj)
173+
Examples
174+
--------
175+
>>> import dpnp as np
176+
>>> a = np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]])
177+
>>> a
178+
array([[ 1, 0, -1],
179+
[ 0, 1, 0],
180+
[ 1, 0, 1]])
181+
>>> np.linalg.cond(a)
182+
array(1.41421356)
183+
>>> np.linalg.cond(a, 'fro')
184+
array(3.16227766)
185+
>>> np.linalg.cond(a, np.inf)
186+
array(2.)
187+
>>> np.linalg.cond(a, -np.inf)
188+
array(1.)
189+
>>> np.linalg.cond(a, 1)
190+
array(2.)
191+
>>> np.linalg.cond(a, -1)
192+
array(1.)
193+
>>> np.linalg.cond(a, 2)
194+
array(1.41421356)
195+
>>> np.linalg.cond(a, -2)
196+
array(0.70710678) # may vary
197+
>>> min(np.linalg.svd(a, compute_uv=False))*min(np.linalg.svd(np.linalg.inv(a), compute_uv=False))
198+
array(0.70710678) # may vary
168199
169-
return result
170-
else:
171-
pass
200+
"""
172201

173-
return call_origin(numpy.linalg.cond, input, p)
202+
dpnp.check_supported_arrays_type(x)
203+
return dpnp_cond(x, p)
174204

175205

176206
def det(a):
@@ -409,6 +439,11 @@ def inv(a):
409439
out : (..., M, M) dpnp.ndarray
410440
(Multiplicative) inverse of the matrix a.
411441
442+
See Also
443+
--------
444+
:obj:`dpnp.linalg.cond` : Compute the condition number of a matrix.
445+
:obj:`dpnp.linalg.svd` : Compute the singular value decomposition.
446+
412447
Examples
413448
--------
414449
>>> import dpnp as np
@@ -710,11 +745,11 @@ def norm(x, ord=None, axis=None, keepdims=False):
710745
[ 2, 3, 4]])
711746
712747
>>> np.linalg.norm(a)
713-
array(7.745966692414834)
748+
array(7.74596669)
714749
>>> np.linalg.norm(b)
715-
array(7.745966692414834)
750+
array(7.74596669)
716751
>>> np.linalg.norm(b, 'fro')
717-
array(7.745966692414834)
752+
array(7.74596669)
718753
>>> np.linalg.norm(a, np.inf)
719754
array(4.)
720755
>>> np.linalg.norm(b, np.inf)
@@ -733,16 +768,16 @@ def norm(x, ord=None, axis=None, keepdims=False):
733768
>>> np.linalg.norm(b, -1)
734769
array(6.)
735770
>>> np.linalg.norm(a, 2)
736-
array(7.745966692414834)
771+
array(7.74596669)
737772
>>> np.linalg.norm(b, 2)
738-
array(7.3484692283495345)
773+
array(7.34846923)
739774
740775
>>> np.linalg.norm(a, -2)
741776
array(0.)
742777
>>> np.linalg.norm(b, -2)
743-
array(1.8570331885190563e-016) # may vary
778+
array(4.35106603e-18) # may vary
744779
>>> np.linalg.norm(a, 3)
745-
array(5.8480354764257312) # may vary
780+
array(5.84803548) # may vary
746781
>>> np.linalg.norm(a, -3)
747782
array(0.)
748783
@@ -763,7 +798,7 @@ def norm(x, ord=None, axis=None, keepdims=False):
763798
>>> np.linalg.norm(m, axis=(1,2))
764799
array([ 3.74165739, 11.22497216])
765800
>>> np.linalg.norm(m[0, :, :]), np.linalg.norm(m[1, :, :])
766-
(array(3.7416573867739413), array(11.224972160321824))
801+
(array(3.74165739), array(11.22497216))
767802
768803
"""
769804

dpnp/linalg/dpnp_utils_linalg.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"check_stacked_2d",
3939
"check_stacked_square",
4040
"dpnp_cholesky",
41+
"dpnp_cond",
4142
"dpnp_det",
4243
"dpnp_eigh",
4344
"dpnp_inv",
@@ -199,6 +200,11 @@ def _common_inexact_type(default_dtype, *dtypes):
199200
return dpnp.result_type(*inexact_dtypes)
200201

201202

203+
def _is_empty_2d(arr):
204+
# check size first for efficiency
205+
return arr.size == 0 and numpy.prod(arr.shape[-2:]) == 0
206+
207+
202208
def _lu_factor(a, res_type):
203209
"""
204210
Compute pivoted LU decomposition.
@@ -841,6 +847,36 @@ def dpnp_cholesky(a, upper):
841847
return a_h
842848

843849

850+
def dpnp_cond(x, p=None):
851+
"""Compute the condition number of a matrix."""
852+
853+
if _is_empty_2d(x):
854+
raise dpnp.linalg.LinAlgError("cond is not defined on empty arrays")
855+
if p is None or p == 2 or p == -2:
856+
s = dpnp.linalg.svd(x, compute_uv=False)
857+
if p == -2:
858+
r = s[..., -1] / s[..., 0]
859+
else:
860+
r = s[..., 0] / s[..., -1]
861+
else:
862+
result_t = _common_type(x)
863+
# The result array will contain nans in the entries
864+
# where inversion failed
865+
invx = dpnp.linalg.inv(x)
866+
r = dpnp.linalg.norm(x, p, axis=(-2, -1)) * dpnp.linalg.norm(
867+
invx, p, axis=(-2, -1)
868+
)
869+
r = r.astype(result_t, copy=False)
870+
871+
# Convert nans to infs unless the original array had nan entries
872+
nan_mask = dpnp.isnan(r)
873+
if nan_mask.any():
874+
nan_mask &= ~dpnp.isnan(x).any(axis=(-2, -1))
875+
r[nan_mask] = dpnp.inf
876+
877+
return r
878+
879+
844880
def dpnp_det(a):
845881
"""
846882
dpnp_det(a)
@@ -1255,18 +1291,18 @@ def dpnp_multi_dot(n, arrays, out=None):
12551291
"""Compute the dot product of two or more arrays in a single function call."""
12561292

12571293
if not arrays[0].ndim in [1, 2]:
1258-
raise numpy.linalg.LinAlgError(
1294+
raise dpnp.linalg.LinAlgError(
12591295
f"{arrays[0].ndim}-dimensional array given. First array must be 1-D or 2-D."
12601296
)
12611297

12621298
if not arrays[-1].ndim in [1, 2]:
1263-
raise numpy.linalg.LinAlgError(
1299+
raise dpnp.linalg.LinAlgError(
12641300
f"{arrays[-1].ndim}-dimensional array given. Last array must be 1-D or 2-D."
12651301
)
12661302

12671303
for arr in arrays[1:-1]:
12681304
if arr.ndim != 2:
1269-
raise numpy.linalg.LinAlgError(
1305+
raise dpnp.linalg.LinAlgError(
12701306
f"{arr.ndim}-dimensional array given. Inner arrays must be 2-D."
12711307
)
12721308

@@ -1401,13 +1437,9 @@ def dpnp_pinv(a, rcond=1e-15, hermitian=False):
14011437
14021438
"""
14031439

1404-
if a.size == 0:
1440+
if _is_empty_2d(a):
14051441
m, n = a.shape[-2:]
1406-
if m == 0 or n == 0:
1407-
res_type = a.dtype
1408-
else:
1409-
res_type = _common_type(a)
1410-
return dpnp.empty_like(a, shape=(a.shape[:-2] + (n, m)), dtype=res_type)
1442+
return dpnp.empty_like(a, shape=(a.shape[:-2] + (n, m)))
14111443

14121444
if dpnp.is_supported_array_type(rcond):
14131445
# Check that `a` and `rcond` are allocated on the same device
@@ -1417,7 +1449,7 @@ def dpnp_pinv(a, rcond=1e-15, hermitian=False):
14171449
# Allocate dpnp.ndarray if rcond is a scalar
14181450
rcond = dpnp.array(rcond, usm_type=a.usm_type, sycl_queue=a.sycl_queue)
14191451

1420-
u, s, vt = dpnp_svd(a.conj(), full_matrices=False, hermitian=hermitian)
1452+
u, s, vt = dpnp_svd(dpnp.conj(a), full_matrices=False, hermitian=hermitian)
14211453

14221454
# discard small singular values
14231455
cutoff = rcond * dpnp.max(s, axis=-1)

tests/skipped_tests.tbl

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,6 @@ tests/third_party/cupy/fft_tests/test_fft.py::TestFftn_param_23_{axes=None, norm
3737

3838
tests/third_party/intel/test_zero_copy_test1.py::test_dpnp_interaction_with_dpctl_memory
3939

40-
tests/test_linalg.py::test_cond[-1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
41-
tests/test_linalg.py::test_cond[1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
42-
tests/test_linalg.py::test_cond[-2-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
43-
tests/test_linalg.py::test_cond[2-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
44-
tests/test_linalg.py::test_cond[-2-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
45-
tests/test_linalg.py::test_cond[2-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
46-
tests/test_linalg.py::test_cond["fro"-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
47-
tests/test_linalg.py::test_cond["fro"-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
48-
tests/test_linalg.py::test_cond[None-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
49-
tests/test_linalg.py::test_cond[None-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
50-
tests/test_linalg.py::test_cond[-numpy.inf-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
51-
tests/test_linalg.py::test_cond[numpy.inf-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
52-
5340
tests/test_linalg.py::test_matrix_rank[None-[0, 1]-float64]
5441
tests/test_linalg.py::test_matrix_rank[None-[0, 1]-float32]
5542
tests/test_linalg.py::test_matrix_rank[None-[0, 1]-int64]

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,6 @@ tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMult
158158

159159
tests/third_party/intel/test_zero_copy_test1.py::test_dpnp_interaction_with_dpctl_memory
160160

161-
tests/test_linalg.py::test_cond[-1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
162-
tests/test_linalg.py::test_cond[1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
163-
tests/test_linalg.py::test_cond[-2-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
164-
tests/test_linalg.py::test_cond[2-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
165-
tests/test_linalg.py::test_cond[-2-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
166-
tests/test_linalg.py::test_cond[2-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
167-
tests/test_linalg.py::test_cond["fro"-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
168-
tests/test_linalg.py::test_cond["fro"-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
169-
tests/test_linalg.py::test_cond[None-[[1, 0, -1], [0, 1, 0], [1, 0, 1]]]
170-
tests/test_linalg.py::test_cond[None-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
171-
tests/test_linalg.py::test_cond[-numpy.inf-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
172-
tests/test_linalg.py::test_cond[numpy.inf-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
173-
174161
tests/test_linalg.py::test_matrix_rank[None-[0, 1]-float64]
175162
tests/test_linalg.py::test_matrix_rank[None-[0, 1]-float32]
176163
tests/test_linalg.py::test_matrix_rank[None-[0, 1]-int64]

0 commit comments

Comments
 (0)