Repository: cupy/cupy

Issue:
Doctest of `lu_factor` is not valid with newer CUDA
### Conditions

<details><summary>the output of `python -c 'import cupy; cupy.show_config()'`</summary>

```
OS                           : Linux-4.15.0-58-generic-x86_64-with-glibc2.27
CuPy Version                 : 9.0.0a1
NumPy Version                : 1.19.2
SciPy Version                : 1.5.2
Cython Build Version         : 0.29.21
Cython Runtime Version       : 0.29.21
CUDA Root                    : /usr/local/cuda
CUDA Build Version           : 10010
CUDA Driver Version          : 11010
CUDA Runtime Version         : 10010
cuBLAS Version               : 10201
cuFFT Version                : 10101
cuRAND Version               : 10101
cuSOLVER Version             : (10, 2, 0)
cuSPARSE Version             : 10300
NVRTC Version                : (10, 1)
Thrust Version               : 100906
CUB Build Version            : 100800
cuDNN Build Version          : 7500
cuDNN Version                : 7500
NCCL Build Version           : 2703
NCCL Runtime Version         : 2703
cuTENSOR Version             : 10200
Device 0 Name                : Tesla V100-SXM2-16GB
Device 0 Compute Capability  : 70
Device 1 Name                : Tesla V100-SXM2-16GB
Device 1 Compute Capability  : 70
Device 2 Name                : Tesla V100-SXM2-16GB
Device 2 Compute Capability  : 70
Device 3 Name                : Tesla V100-SXM2-16GB
Device 3 Compute Capability  : 70
Device 4 Name                : Tesla V100-SXM2-16GB
Device 4 Compute Capability  : 70
Device 5 Name                : Tesla V100-SXM2-16GB
Device 5 Compute Capability  : 70
Device 6 Name                : Tesla V100-SXM2-16GB
Device 6 Compute Capability  : 70
Device 7 Name                : Tesla V100-SXM2-16GB
Device 7 Compute Capability  : 70
```

</details>

### Code to reproduce

`cd docs; make doctest`

### Error messages, stack traces, or logs

```
Document: reference/generated/cupyx.scipy.linalg.lu_factor
----------------------------------------------------------
<doctest default[2]>:1: LinAlgWarning: Diagonal number 1 is exactly zero. Singular matrix.
  scipy.linalg.lu_factor(np.array([[0, 1], [0, 0]], dtype=np.float32))
/mnt/vol21/kataoka/github/cupy/cupyx/scipy/linalg/decomp_lu.py:59: RuntimeWarning: Diagonal number 1 is exactly zero. Singular matrix.
  return _lu_factor(a, overwrite_a, check_finite)
**********************************************************************
File "reference/generated/cupyx.scipy.linalg.lu_factor.rst", line ?, in default
Failed example:
    cupyx.scipy.linalg.lu_factor(cp.array([[0, 1], [0, 0]], dtype=cp.float32))
Expected:
    (array([[ 0.,  1.],
           [nan, nan]], dtype=float32), array([0, 1], dtype=int32))
Got:
    (array([[0., 1.],
           [0., 0.]], dtype=float32), array([ 0, -1], dtype=int32))
**********************************************************************
1 items had failures:
   1 of   6 in default
6 tests in 1 items.
5 passed and 1 failed.
***Test Failed*** 1 failures.
```


Discussion:

Resolving patch:
diff --git a/cupyx/scipy/linalg/decomp_lu.py b/cupyx/scipy/linalg/decomp_lu.py
--- a/cupyx/scipy/linalg/decomp_lu.py
+++ b/cupyx/scipy/linalg/decomp_lu.py
@@ -35,26 +35,6 @@ def lu_factor(a, overwrite_a=False, check_finite=True):
             ``i`` of the matrix was interchanged with row ``piv[i]``
 
     .. seealso:: :func:`scipy.linalg.lu_factor`
-
-    .. note::
-
-        Current implementation returns result different from SciPy when the
-        matrix singular. SciPy returns an array containing ``0.`` while the
-        current implementation returns an array containing ``nan``.
-
-        >>> import numpy as np
-        >>> import scipy.linalg
-        >>> scipy.linalg.lu_factor(np.array([[0, 1], [0, 0]], \
-dtype=np.float32))
-        (array([[0., 1.],
-               [0., 0.]], dtype=float32), array([0, 1], dtype=int32))
-
-        >>> import cupy as cp
-        >>> import cupyx.scipy.linalg
-        >>> cupyx.scipy.linalg.lu_factor(cp.array([[0, 1], [0, 0]], \
-dtype=cp.float32))
-        (array([[ 0.,  1.],
-               [nan, nan]], dtype=float32), array([0, 1], dtype=int32))
     """
     return _lu_factor(a, overwrite_a, check_finite)
 


Test patch:
diff --git a/tests/cupyx_tests/scipy_tests/linalg_tests/test_decomp_lu.py b/tests/cupyx_tests/scipy_tests/linalg_tests/test_decomp_lu.py
--- a/tests/cupyx_tests/scipy_tests/linalg_tests/test_decomp_lu.py
+++ b/tests/cupyx_tests/scipy_tests/linalg_tests/test_decomp_lu.py
@@ -1,4 +1,5 @@
 import unittest
+import warnings
 
 import numpy
 import cupy
@@ -31,10 +32,8 @@ def test_lu_factor(self, dtype):
         cupy.testing.assert_allclose(result_cpu[0], result_gpu[0], atol=1e-5)
         cupy.testing.assert_array_equal(result_cpu[1], result_gpu[1])
 
-    @testing.for_dtypes('fdFD')
-    def test_lu_factor_reconstruction(self, dtype):
+    def check_lu_factor_reconstruction(self, A):
         m, n = self.shape
-        A = testing.shaped_random(self.shape, cupy, dtype=dtype)
         lu, piv = cupyx.scipy.linalg.lu_factor(A)
         # extract ``L`` and ``U`` from ``lu``
         L = cupy.tril(lu, k=-1)
@@ -58,6 +57,22 @@ def test_lu_factor_reconstruction(self, dtype):
         LU = L.dot(U)
         cupy.testing.assert_allclose(LU, PA, atol=1e-5)
 
+    @testing.for_dtypes('fdFD')
+    def test_lu_factor_reconstruction(self, dtype):
+        A = testing.shaped_random(self.shape, cupy, dtype=dtype)
+        self.check_lu_factor_reconstruction(A)
+
+    @testing.for_dtypes('fdFD')
+    def test_lu_factor_reconstruction_singular(self, dtype):
+        if self.shape[0] != self.shape[1]:
+            return unittest.SkipTest()
+        A = testing.shaped_random(self.shape, cupy, dtype=dtype)
+        A -= A.mean(axis=0, keepdims=True)
+        A -= A.mean(axis=1, keepdims=True)
+        with warnings.catch_warnings():
+            warnings.simplefilter('ignore', RuntimeWarning)
+            self.check_lu_factor_reconstruction(A)
+
 
 @testing.gpu
 @testing.parameterize(*testing.product({