Repository: cupy/cupy
Pull request: add support for `ord` = 2, -2, and 'nuc' in `cupy.linalg.norm`
closes #3053 

Problem statement:
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.

Hints from issue discussion:
I would like to tackle this.

Will be opening a PR soon!

Commit message: solve import error

Patch to review:
diff --git a/cupy/linalg/norms.py b/cupy/linalg/norms.py
index 873f246b965..43b55d1e217 100644
--- a/cupy/linalg/norms.py
+++ b/cupy/linalg/norms.py
@@ -6,6 +6,30 @@
 from cupy.linalg import util
 
 
+def _multi_svd_norm(x, row_axis, col_axis, op):
+    """Compute a function of the singular values of the 2-D matrices in `x`.
+    This is a private utility function used by `cumpy.linalg.norm()`.
+    Parameters
+    ----------
+    x : cupy.ndarray
+    row_axis, col_axis : int
+        The axes of `x` that hold the 2-D matrices.
+    op : callable
+        This should be either `cupy.amin` or `cumpy.amax` or `cumpy.sum`.
+    Returns
+    -------
+    result : float or cupy.ndarray
+        If `x` is 2-D, the return values is a float.
+        Otherwise, it is an array with ``x.ndim - 2`` dimensions.
+        The return values are either the minimum or maximum or sum of the
+        singular values of the matrices, depending on whether `op`
+        is `cupy.amin` or `cupy.amax` or `cupy.sum`.
+    """
+    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 +121,11 @@ 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:
+            ret = _multi_svd_norm(x, row_axis, col_axis, cupy.amax)
+        elif ord == -2:
+            ret = _multi_svd_norm(x, row_axis, col_axis, cupy.amin)
+        elif ord == 1:
             if col_axis > row_axis:
                 col_axis -= 1
             ret = abs(x).sum(axis=row_axis).max(axis=col_axis)
@@ -120,6 +148,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:


Code review comments:
diff_hunk: @@ -6,6 +6,30 @@
 from cupy.linalg import util
 
 
+def _multi_svd_norm(x, row_axis, col_axis, op):
+    """Compute a function of the singular values of the 2-D matrices in `x`.
+    This is a private utility function used by `cumpy.linalg.norm()`.
+    Parameters
+    ----------
+    x : cupy.ndarray
+    row_axis, col_axis : int
+        The axes of `x` that hold the 2-D matrices.
+    op : callable
+        This should be either `cupy.amin` or `cumpy.amax` or `cumpy.sum`.
+    Returns
+    -------
+    result : float or cupy.ndarray
+        If `x` is 2-D, the return values is a float.
+        Otherwise, it is an array with ``x.ndim - 2`` dimensions.
+        The return values are either the minimum or maximum or sum of the
+        singular values of the matrices, depending on whether `op`
+        is `cupy.amin` or `cupy.amax` or `cupy.sum`.
+    """
path: cupy/linalg/norms.py
text: @user1:
I'd suggest removing the entire docstring for a few reasons:
1. This is only a private function and not exposed to end users
2. This helper function is simple enough (only two lines)
3. The docstring format does not match the CuPy (or Google) style

For the docstring style, you can take a look at `norm()`'s docstring right below for how to properly format it.

@author:
I have removed that docstring.
And I will take care of that docstring convention next time onwards : )