Repository: cupy/cupy

Issue:
Got ValueError when `ord` in `cupy.linalg.norm()` is 2 or -2
Reproducer:
```python
>>> import numpy as np
>>> a  = [[2, 0, 1], [-1, 1, 0], [-3, 3, 0]]
>>> a = np.asarray(a, dtype=np.float64)
>>> np.linalg.norm(a, ord=2)
4.723421263784789
>>>
>>> import cupy as cp
>>> b = cp.asarray(a)
>>> cp.linalg.norm(b, ord=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/leofang/test/cupy2/cupy/linalg/norms.py", line 124, in norm
    raise ValueError('Invalid norm order for matrices.')
ValueError: Invalid norm order for matrices.
```
`ord=2` returns the largest singular value and `-2` the smallest. 


Discussion:
I would like to tackle this.

Will be opening a PR soon!

Resolving patch:
diff --git a/cupy/linalg/norms.py b/cupy/linalg/norms.py
--- a/cupy/linalg/norms.py
+++ b/cupy/linalg/norms.py
@@ -5,6 +5,14 @@
 from cupy.linalg import decomposition
 from cupy.linalg import util
 
+import functools
+
+
+def _multi_svd_norm(x, row_axis, col_axis, op):
+    y = cupy.moveaxis(x, (row_axis, col_axis), (-2, -1))
+    result = op(decomposition.svd(y, compute_uv=False), axis=-1)
+    return result
+
 
 def norm(x, ord=None, axis=None, keepdims=False):
     """Returns one of matrix norms specified by ``ord`` parameter.
@@ -97,7 +105,13 @@ def norm(x, ord=None, axis=None, keepdims=False):
                              (axis, x.shape))
         if row_axis == col_axis:
             raise ValueError('Duplicate axes given.')
-        if ord == 1:
+        if ord == 2:
+            op_max = functools.partial(cupy.take, indices=0)
+            ret = _multi_svd_norm(x, row_axis, col_axis, op_max)
+        elif ord == -2:
+            op_min = functools.partial(cupy.take, indices=-1)
+            ret = _multi_svd_norm(x, row_axis, col_axis, op_min)
+        elif ord == 1:
             if col_axis > row_axis:
                 col_axis -= 1
             ret = abs(x).sum(axis=row_axis).max(axis=col_axis)
@@ -120,6 +134,8 @@ def norm(x, ord=None, axis=None, keepdims=False):
                 ret = cupy.sqrt(s.sum(axis=axis))
             else:
                 ret = cupy.sqrt((x * x).sum(axis=axis))
+        elif ord == 'nuc':
+            ret = _multi_svd_norm(x, row_axis, col_axis, cupy.sum)
         else:
             raise ValueError('Invalid norm order for matrices.')
         if keepdims:


Test patch:
diff --git a/tests/cupy_tests/linalg_tests/test_norms.py b/tests/cupy_tests/linalg_tests/test_norms.py
--- a/tests/cupy_tests/linalg_tests/test_norms.py
+++ b/tests/cupy_tests/linalg_tests/test_norms.py
@@ -30,7 +30,7 @@ def test_external_trace(self, xp, dtype):
     'keepdims': [True, False],
 }) + testing.product({
     'shape': [(1, 2), (2, 2)],
-    'ord': [-numpy.Inf, -1, 1, numpy.Inf, 'fro'],
+    'ord': [-numpy.Inf, -2, -1, 1, 2, numpy.Inf, 'fro', 'nuc'],
     'axis': [(0, 1), None],
     'keepdims': [True, False],
 }) + testing.product({
@@ -51,12 +51,11 @@ class TestNorm(unittest.TestCase):
     # TODO(kmaehashi) Currently dtypes returned from CuPy is not compatible
     # with NumPy. We should remove `type_check=False` once NumPy is fixed.
     # See https://github.com/cupy/cupy/pull/875 for details.
-    @testing.for_all_dtypes()
+    @testing.for_all_dtypes(no_float16=True)
     @testing.numpy_cupy_allclose(rtol=1e-3, atol=1e-4, type_check=False)
     def test_norm(self, xp, dtype):
         a = testing.shaped_arange(self.shape, xp, dtype)
-        with testing.NumpyError(divide='ignore'):
-            return xp.linalg.norm(a, self.ord, self.axis, self.keepdims)
+        return xp.linalg.norm(a, self.ord, self.axis, self.keepdims)
 
 
 @testing.parameterize(*testing.product({