/
_norm.py
111 lines (98 loc) · 4.01 KB
/
_norm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import numpy
import cupy
import cupyx.scipy.sparse
def _sparse_frobenius_norm(x):
if cupy.issubdtype(x.dtype, cupy.complexfloating):
sqnorm = abs(x).power(2).sum()
else:
sqnorm = x.power(2).sum()
return cupy.sqrt(sqnorm)
def norm(x, ord=None, axis=None):
"""Norm of a cupy.scipy.spmatrix
This function is able to return one of seven different sparse matrix norms,
depending on the value of the ``ord`` parameter.
Args:
x (sparse matrix) : Input sparse matrix.
ord (non-zero int, inf, -inf, 'fro', optional) : Order of the norm (see
table under ``Notes``). inf means numpy's `inf` object.
axis : (int, 2-tuple of ints, None, optional): If `axis` is an
integer, it specifies the axis of `x` along which to
compute the vector norms. If `axis` is a 2-tuple, it specifies the
axes that hold 2-D matrices, and the matrix norms of these matrices
are computed. If `axis` is None then either a vector norm
(when `x` is 1-D) or a matrix norm (when `x` is 2-D) is returned.
Returns:
ndarray : 0-D or 1-D array or norm(s).
.. seealso:: :func:`scipy.sparse.linalg.norm`
"""
if not cupyx.scipy.sparse.issparse(x):
raise TypeError(("input is not sparse. use cupy.linalg.norm"))
# Check the default case first and handle it immediately.
if axis is None and ord in (None, 'fro', 'f'):
return _sparse_frobenius_norm(x)
# Some norms require functions that are not implemented for all types.
x = x.tocsr()
if axis is None:
axis = (0, 1)
elif not isinstance(axis, tuple):
msg = "'axis' must be None, an integer or a tuple of integers"
try:
int_axis = int(axis)
except TypeError:
raise TypeError(msg)
if axis != int_axis:
raise TypeError(msg)
axis = (int_axis,)
nd = 2
if len(axis) == 2:
row_axis, col_axis = axis
if not (-nd <= row_axis < nd and -nd <= col_axis < nd):
raise ValueError('Invalid axis %r for an array with shape %r' %
(axis, x.shape))
if row_axis % nd == col_axis % nd:
raise ValueError('Duplicate axes given.')
if ord == 2:
raise NotImplementedError
# return _multi_svd_norm(x, row_axis, col_axis, amax)
elif ord == -2:
raise NotImplementedError
# return _multi_svd_norm(x, row_axis, col_axis, amin)
elif ord == 1:
return abs(x).sum(axis=row_axis).max()
elif ord == numpy.Inf:
return abs(x).sum(axis=col_axis).max()
elif ord == -1:
return abs(x).sum(axis=row_axis).min()
elif ord == -numpy.Inf:
return abs(x).sum(axis=col_axis).min()
elif ord in (None, 'f', 'fro'):
# The axis order does not matter for this norm.
return _sparse_frobenius_norm(x)
else:
raise ValueError("Invalid norm order for matrices.")
elif len(axis) == 1:
a, = axis
if not (-nd <= a < nd):
raise ValueError('Invalid axis %r for an array with shape %r' %
(axis, x.shape))
if ord == numpy.Inf:
return abs(x).max(axis=a).A.ravel()
elif ord == -numpy.Inf:
return abs(x).min(axis=a).A.ravel()
elif ord == 0:
# Zero norm
return (x != 0).astype(numpy.float32).sum(axis=a).ravel().astype(
numpy.int_)
elif ord == 1:
# special case for speedup
return abs(x).sum(axis=a).ravel()
elif ord in (2, None):
return cupy.sqrt(abs(x).power(2).sum(axis=a)).ravel()
else:
try:
ord + 1
except TypeError:
raise ValueError('Invalid norm order for vectors.')
return cupy.power(abs(x).power(ord).sum(axis=a), 1 / ord).ravel()
else:
raise ValueError("Improper number of dimensions to norm.")