Repository: cupy/cupy

Issue:
cp.linalg.inv does not support a complex-valued matrix
I sincerely appreciate the outstanding efforts having been devoted to this project. It seems that cp.linalg.inv does not support a complex-valued matrix. For example, one can reproduce the same situation as follows:

    import numpy as np
    A = np.eye(1) + 1.j
    print(A.dot(np.linalg.inv(A))) #=> [[1.+0.j]]
    A = np.eye(2) + 1.j
    print(A.dot(np.linalg.inv(A)))  # => [[1.+5.55111512e-17j, 0.+2.77555756e-17j], [0.+5.55111512e-17j, 1.+5.55111512e-17j]]
    # This NumPy implementation returns the identity matrix.

    import cupy as cp
    A = cp.eye(1) + 1.j
    print(A.dot(cp.linalg.inv(A))) #=> [[1.+1.j]]
    A = cp.eye(2) + 1.j
    print(A.dot(cp.linalg.inv(A)))  # => [[2.+0.j, 0.+1.j], [1.+1.j, 1.+1.j]]
    # This CuPy implementation returns somewhat strange matrices.

According to the current implementation, cp.linalg.inv supports the dtypes of 'f' and 'd' only, although the getrf and getrs functions originally support the data types of cuComplex and cuDoubleComplex.
https://github.com/cupy/cupy/blob/master/cupy/linalg/solve.py#L236

The followings are my environment information.

    $ cat /etc/os-release
    NAME="Ubuntu"
    VERSION="18.04.3 LTS (Bionic Beaver)"

    $ python -c 'import cupy; cupy.show_config()'
    CuPy Version          : 6.3.0
    CUDA Root             : /usr/local/cuda-10.1
    CUDA Build Version    : 10010
    CUDA Driver Version   : 10010
    CUDA Runtime Version  : 10010
    cuDNN Build Version   : 7602
    cuDNN Version         : 7602
    NCCL Build Version    : 2402
    NCCL Runtime Version  : 2402

It is highly appreciated if CuPy supports the avobe complex-valued operation. Many thanks in advance.


Discussion:
possible dup: #2071
Dear @leofang, I appreciate your immediate notice. Since this cp.linalg.inv issue has been already labeled as `contribution-welcome`, I would like to close this one -- with thanks.
it doesn't hurt to keep it open I think. Multiple dup issues could perhaps help prioritize things and signal the need.

Resolving patch:
diff --git a/cupy/linalg/solve.py b/cupy/linalg/solve.py
--- a/cupy/linalg/solve.py
+++ b/cupy/linalg/solve.py
@@ -260,7 +260,8 @@ def inv(a):
     util._assert_rank2(a)
     util._assert_nd_squareness(a)
 
-    if a.dtype.char == 'f' or a.dtype.char == 'd':
+    # support float32, float64, complex64, and complex128
+    if a.dtype.char in 'fdFD':
         dtype = a.dtype.char
     else:
         dtype = numpy.find_common_type((a.dtype.char, 'f'), ()).char
@@ -274,10 +275,20 @@ def inv(a):
         getrf = cusolver.sgetrf
         getrf_bufferSize = cusolver.sgetrf_bufferSize
         getrs = cusolver.sgetrs
-    else:  # dtype == 'd'
+    elif dtype == 'd':
         getrf = cusolver.dgetrf
         getrf_bufferSize = cusolver.dgetrf_bufferSize
         getrs = cusolver.dgetrs
+    elif dtype == 'F':
+        getrf = cusolver.cgetrf
+        getrf_bufferSize = cusolver.cgetrf_bufferSize
+        getrs = cusolver.cgetrs
+    elif dtype == 'D':
+        getrf = cusolver.zgetrf
+        getrf_bufferSize = cusolver.zgetrf_bufferSize
+        getrs = cusolver.zgetrs
+    else:
+        raise ValueError('unsupported dtype')
 
     m = a.shape[0]
 


Test patch:
diff --git a/tests/cupy_tests/linalg_tests/test_solve.py b/tests/cupy_tests/linalg_tests/test_solve.py
--- a/tests/cupy_tests/linalg_tests/test_solve.py
+++ b/tests/cupy_tests/linalg_tests/test_solve.py
@@ -82,7 +82,7 @@ def test_tensorsolve(self, xp):
 @testing.gpu
 class TestInv(unittest.TestCase):
 
-    @testing.for_float_dtypes(no_float16=True)
+    @testing.for_dtypes('fdFD')
     @condition.retry(10)
     def check_x(self, a_shape, dtype):
         a_cpu = numpy.random.randint(0, 10, size=a_shape).astype(dtype)