We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
F.inv behaves differently between CPU/GPU
Currently, F.inv ignores error flags (info) from cuBLAS.

https://github.com/chainer/chainer/blob/v3.0.0/chainer/functions/math/inv.py#L56

As a result, it behaves differently between CPU/GPU.

When CPU mode:

```py
chainer.functions.inv(np.zeros(4, dtype=np.float32).reshape((2,2)))
```

raises exception (`numpy.linalg.linalg.LinAlgError: Singular matrix`), while GPU mode:

```py
chainer.functions.inv(cp.zeros(4, dtype=np.float32).reshape((2,2)))
```

no exception occurs and

```py
variable([[ nan,  nan],
          [ nan,  inf]])
```

is returned.

I think it is better to change GPU mode behavior to raise Exception, so that users can notice the error immediately.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `chainer/functions/math/inv.py`
Content:
```
1 import numpy.linalg
2 
3 from chainer import cuda
4 from chainer import function_node
5 import chainer.functions
6 from chainer.functions.math import matmul
7 from chainer import utils
8 from chainer.utils import type_check
9 
10 
11 def _inv_gpu(b):
12     # We do a batched LU decomposition on the GPU to compute the inverse
13     # Change the shape of the array to be size=1 minibatch if necessary
14     # Also copy the matrix as the elments will be modified in-place
15     a = matmul._as_batch_mat(b).copy()
16     n = a.shape[1]
17     n_matrices = len(a)
18     # Pivot array
19     p = cuda.cupy.empty((n, n_matrices), dtype=numpy.int32)
20     # Output array
21     c = cuda.cupy.empty_like(a)
22     # These arrays hold information on the execution success
23     # or if the matrix was singular
24     info = cuda.cupy.empty(n_matrices, dtype=numpy.int32)
25     ap = matmul._mat_ptrs(a)
26     cp = matmul._mat_ptrs(c)
27     _, lda = matmul._get_ld(a)
28     _, ldc = matmul._get_ld(c)
29     handle = cuda.Device().cublas_handle
30     cuda.cublas.sgetrfBatched(
31         handle, n, ap.data.ptr, lda, p.data.ptr, info.data.ptr, n_matrices)
32     cuda.cublas.sgetriBatched(
33         handle, n, ap.data.ptr, lda, p.data.ptr, cp.data.ptr, ldc,
34         info.data.ptr, n_matrices)
35     return c, info
36 
37 
38 class Inv(function_node.FunctionNode):
39 
40     def check_type_forward(self, in_types):
41         type_check.expect(in_types.size() == 1)
42         a_type, = in_types
43         type_check.expect(a_type.dtype == numpy.float32)
44         # Only 2D array shapes allowed
45         type_check.expect(a_type.ndim == 2)
46         # Matrix inversion only allowed for square matrices
47         type_check.expect(a_type.shape[0] == a_type.shape[1])
48 
49     def forward_cpu(self, x):
50         self.retain_outputs((0,))
51         invx = utils.force_array(numpy.linalg.inv(x[0]))
52         return invx,
53 
54     def forward_gpu(self, x):
55         self.retain_outputs((0,))
56         shape = x[0].shape
57         invx = _inv_gpu(x[0].reshape(1, *shape))[0].reshape(shape)
58         return invx,
59 
60     def backward(self, x, gy):
61         invx, = self.get_retained_outputs()
62         # Gradient is - x^-T (dx) x^-T
63         invxT = chainer.functions.transpose(invx)
64         gx = chainer.functions.matmul(
65             chainer.functions.matmul(- invxT, gy[0]), invxT)
66         return gx,
67 
68 
69 class BatchInv(function_node.FunctionNode):
70 
71     def check_type_forward(self, in_types):
72         type_check.expect(in_types.size() == 1)
73         a_type, = in_types
74         type_check.expect(a_type.dtype == numpy.float32)
75         # Only a minibatch of 2D array shapes allowed
76         type_check.expect(a_type.ndim == 3)
77         # Matrix inversion only allowed for square matrices
78         # so assert the last two dimensions are equal
79         type_check.expect(a_type.shape[-1] == a_type.shape[-2])
80 
81     def forward_cpu(self, x):
82         self.retain_outputs((0,))
83         invx = utils.force_array(numpy.linalg.inv(x[0]))
84         return invx,
85 
86     def forward_gpu(self, x):
87         self.retain_outputs((0,))
88         invx, _ = _inv_gpu(x[0])
89         return invx,
90 
91     def backward(self, x, gy):
92         invx, = self.get_retained_outputs()
93         # Unpack 1-length tuples
94         gy, = gy
95         # Gradient is - x^-T (dx) x^-T
96         ret = chainer.functions.matmul(-invx, gy, transa=True)
97         ret2 = chainer.functions.matmul(ret, invx, transb=True)
98         return ret2,
99 
100 
101 def inv(a):
102     """Computes the inverse of square matrix.
103 
104     Args:
105         a (Variable): Input array to compute the inverse for. Shape of
106             the array should be ``(n, n)`` where ``n`` is the dimensionality of
107             a square matrix.
108 
109     Returns:
110         ~chainer.Variable: Matrix inverse of ``a``.
111     """
112     return Inv().apply((a,))[0]
113 
114 
115 def batch_inv(a):
116     """Computes the inverse of a batch of square matrices.
117 
118     Args:
119         a (Variable): Input array to compute the inverse for. Shape of
120             the array should be ``(m, n, n)`` where ``m`` is the number of
121             matrices in the batch, and ``n`` is the dimensionality of a square
122             matrix.
123 
124     Returns:
125         ~chainer.Variable: Inverse of every matrix in the batch of matrices.
126     """
127     return BatchInv().apply((a,))[0]
128
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/chainer/functions/math/inv.py b/chainer/functions/math/inv.py
--- a/chainer/functions/math/inv.py
+++ b/chainer/functions/math/inv.py
@@ -1,5 +1,6 @@
 import numpy.linalg
 
+import chainer
 from chainer import cuda
 from chainer import function_node
 import chainer.functions
@@ -54,7 +55,11 @@
     def forward_gpu(self, x):
         self.retain_outputs((0,))
         shape = x[0].shape
-        invx = _inv_gpu(x[0].reshape(1, *shape))[0].reshape(shape)
+        invx, info = _inv_gpu(x[0].reshape(1, *shape))
+        if chainer.is_debug():
+            if cuda.cupy.any(info != 0):
+                raise ValueError('Input has singular matrices.')
+        invx = invx.reshape(shape)
         return invx,
 
     def backward(self, x, gy):
@@ -85,7 +90,10 @@
 
     def forward_gpu(self, x):
         self.retain_outputs((0,))
-        invx, _ = _inv_gpu(x[0])
+        invx, info = _inv_gpu(x[0])
+        if chainer.is_debug():
+            if cuda.cupy.any(info != 0):
+                raise ValueError('Input has singular matrices.')
         return invx,
 
     def backward(self, x, gy):

{"golden_diff": "diff --git a/chainer/functions/math/inv.py b/chainer/functions/math/inv.py\n--- a/chainer/functions/math/inv.py\n+++ b/chainer/functions/math/inv.py\n@@ -1,5 +1,6 @@\n import numpy.linalg\n \n+import chainer\n from chainer import cuda\n from chainer import function_node\n import chainer.functions\n@@ -54,7 +55,11 @@\n     def forward_gpu(self, x):\n         self.retain_outputs((0,))\n         shape = x[0].shape\n-        invx = _inv_gpu(x[0].reshape(1, *shape))[0].reshape(shape)\n+        invx, info = _inv_gpu(x[0].reshape(1, *shape))\n+        if chainer.is_debug():\n+            if cuda.cupy.any(info != 0):\n+                raise ValueError('Input has singular matrices.')\n+        invx = invx.reshape(shape)\n         return invx,\n \n     def backward(self, x, gy):\n@@ -85,7 +90,10 @@\n \n     def forward_gpu(self, x):\n         self.retain_outputs((0,))\n-        invx, _ = _inv_gpu(x[0])\n+        invx, info = _inv_gpu(x[0])\n+        if chainer.is_debug():\n+            if cuda.cupy.any(info != 0):\n+                raise ValueError('Input has singular matrices.')\n         return invx,\n \n     def backward(self, x, gy):\n", "issue": "F.inv behaves differently between CPU/GPU\nCurrently, F.inv ignores error flags (info) from cuBLAS.\r\n\r\nhttps://github.com/chainer/chainer/blob/v3.0.0/chainer/functions/math/inv.py#L56\r\n\r\nAs a result, it behaves differently between CPU/GPU.\r\n\r\nWhen CPU mode:\r\n\r\n```py\r\nchainer.functions.inv(np.zeros(4, dtype=np.float32).reshape((2,2)))\r\n```\r\n\r\nraises exception (`numpy.linalg.linalg.LinAlgError: Singular matrix`), while GPU mode:\r\n\r\n```py\r\nchainer.functions.inv(cp.zeros(4, dtype=np.float32).reshape((2,2)))\r\n```\r\n\r\nno exception occurs and\r\n\r\n```py\r\nvariable([[ nan,  nan],\r\n          [ nan,  inf]])\r\n```\r\n\r\nis returned.\r\n\r\nI think it is better to change GPU mode behavior to raise Exception, so that users can notice the error immediately.\n", "before_files": [{"content": "import numpy.linalg\n\nfrom chainer import cuda\nfrom chainer import function_node\nimport chainer.functions\nfrom chainer.functions.math import matmul\nfrom chainer import utils\nfrom chainer.utils import type_check\n\n\ndef _inv_gpu(b):\n    # We do a batched LU decomposition on the GPU to compute the inverse\n    # Change the shape of the array to be size=1 minibatch if necessary\n    # Also copy the matrix as the elments will be modified in-place\n    a = matmul._as_batch_mat(b).copy()\n    n = a.shape[1]\n    n_matrices = len(a)\n    # Pivot array\n    p = cuda.cupy.empty((n, n_matrices), dtype=numpy.int32)\n    # Output array\n    c = cuda.cupy.empty_like(a)\n    # These arrays hold information on the execution success\n    # or if the matrix was singular\n    info = cuda.cupy.empty(n_matrices, dtype=numpy.int32)\n    ap = matmul._mat_ptrs(a)\n    cp = matmul._mat_ptrs(c)\n    _, lda = matmul._get_ld(a)\n    _, ldc = matmul._get_ld(c)\n    handle = cuda.Device().cublas_handle\n    cuda.cublas.sgetrfBatched(\n        handle, n, ap.data.ptr, lda, p.data.ptr, info.data.ptr, n_matrices)\n    cuda.cublas.sgetriBatched(\n        handle, n, ap.data.ptr, lda, p.data.ptr, cp.data.ptr, ldc,\n        info.data.ptr, n_matrices)\n    return c, info\n\n\nclass Inv(function_node.FunctionNode):\n\n    def check_type_forward(self, in_types):\n        type_check.expect(in_types.size() == 1)\n        a_type, = in_types\n        type_check.expect(a_type.dtype == numpy.float32)\n        # Only 2D array shapes allowed\n        type_check.expect(a_type.ndim == 2)\n        # Matrix inversion only allowed for square matrices\n        type_check.expect(a_type.shape[0] == a_type.shape[1])\n\n    def forward_cpu(self, x):\n        self.retain_outputs((0,))\n        invx = utils.force_array(numpy.linalg.inv(x[0]))\n        return invx,\n\n    def forward_gpu(self, x):\n        self.retain_outputs((0,))\n        shape = x[0].shape\n        invx = _inv_gpu(x[0].reshape(1, *shape))[0].reshape(shape)\n        return invx,\n\n    def backward(self, x, gy):\n        invx, = self.get_retained_outputs()\n        # Gradient is - x^-T (dx) x^-T\n        invxT = chainer.functions.transpose(invx)\n        gx = chainer.functions.matmul(\n            chainer.functions.matmul(- invxT, gy[0]), invxT)\n        return gx,\n\n\nclass BatchInv(function_node.FunctionNode):\n\n    def check_type_forward(self, in_types):\n        type_check.expect(in_types.size() == 1)\n        a_type, = in_types\n        type_check.expect(a_type.dtype == numpy.float32)\n        # Only a minibatch of 2D array shapes allowed\n        type_check.expect(a_type.ndim == 3)\n        # Matrix inversion only allowed for square matrices\n        # so assert the last two dimensions are equal\n        type_check.expect(a_type.shape[-1] == a_type.shape[-2])\n\n    def forward_cpu(self, x):\n        self.retain_outputs((0,))\n        invx = utils.force_array(numpy.linalg.inv(x[0]))\n        return invx,\n\n    def forward_gpu(self, x):\n        self.retain_outputs((0,))\n        invx, _ = _inv_gpu(x[0])\n        return invx,\n\n    def backward(self, x, gy):\n        invx, = self.get_retained_outputs()\n        # Unpack 1-length tuples\n        gy, = gy\n        # Gradient is - x^-T (dx) x^-T\n        ret = chainer.functions.matmul(-invx, gy, transa=True)\n        ret2 = chainer.functions.matmul(ret, invx, transb=True)\n        return ret2,\n\n\ndef inv(a):\n    \"\"\"Computes the inverse of square matrix.\n\n    Args:\n        a (Variable): Input array to compute the inverse for. Shape of\n            the array should be ``(n, n)`` where ``n`` is the dimensionality of\n            a square matrix.\n\n    Returns:\n        ~chainer.Variable: Matrix inverse of ``a``.\n    \"\"\"\n    return Inv().apply((a,))[0]\n\n\ndef batch_inv(a):\n    \"\"\"Computes the inverse of a batch of square matrices.\n\n    Args:\n        a (Variable): Input array to compute the inverse for. Shape of\n            the array should be ``(m, n, n)`` where ``m`` is the number of\n            matrices in the batch, and ``n`` is the dimensionality of a square\n            matrix.\n\n    Returns:\n        ~chainer.Variable: Inverse of every matrix in the batch of matrices.\n    \"\"\"\n    return BatchInv().apply((a,))[0]\n", "path": "chainer/functions/math/inv.py"}], "after_files": [{"content": "import numpy.linalg\n\nimport chainer\nfrom chainer import cuda\nfrom chainer import function_node\nimport chainer.functions\nfrom chainer.functions.math import matmul\nfrom chainer import utils\nfrom chainer.utils import type_check\n\n\ndef _inv_gpu(b):\n    # We do a batched LU decomposition on the GPU to compute the inverse\n    # Change the shape of the array to be size=1 minibatch if necessary\n    # Also copy the matrix as the elments will be modified in-place\n    a = matmul._as_batch_mat(b).copy()\n    n = a.shape[1]\n    n_matrices = len(a)\n    # Pivot array\n    p = cuda.cupy.empty((n, n_matrices), dtype=numpy.int32)\n    # Output array\n    c = cuda.cupy.empty_like(a)\n    # These arrays hold information on the execution success\n    # or if the matrix was singular\n    info = cuda.cupy.empty(n_matrices, dtype=numpy.int32)\n    ap = matmul._mat_ptrs(a)\n    cp = matmul._mat_ptrs(c)\n    _, lda = matmul._get_ld(a)\n    _, ldc = matmul._get_ld(c)\n    handle = cuda.Device().cublas_handle\n    cuda.cublas.sgetrfBatched(\n        handle, n, ap.data.ptr, lda, p.data.ptr, info.data.ptr, n_matrices)\n    cuda.cublas.sgetriBatched(\n        handle, n, ap.data.ptr, lda, p.data.ptr, cp.data.ptr, ldc,\n        info.data.ptr, n_matrices)\n    return c, info\n\n\nclass Inv(function_node.FunctionNode):\n\n    def check_type_forward(self, in_types):\n        type_check.expect(in_types.size() == 1)\n        a_type, = in_types\n        type_check.expect(a_type.dtype == numpy.float32)\n        # Only 2D array shapes allowed\n        type_check.expect(a_type.ndim == 2)\n        # Matrix inversion only allowed for square matrices\n        type_check.expect(a_type.shape[0] == a_type.shape[1])\n\n    def forward_cpu(self, x):\n        self.retain_outputs((0,))\n        invx = utils.force_array(numpy.linalg.inv(x[0]))\n        return invx,\n\n    def forward_gpu(self, x):\n        self.retain_outputs((0,))\n        shape = x[0].shape\n        invx, info = _inv_gpu(x[0].reshape(1, *shape))\n        if chainer.is_debug():\n            if cuda.cupy.any(info != 0):\n                raise ValueError('Input has singular matrices.')\n        invx = invx.reshape(shape)\n        return invx,\n\n    def backward(self, x, gy):\n        invx, = self.get_retained_outputs()\n        # Gradient is - x^-T (dx) x^-T\n        invxT = chainer.functions.transpose(invx)\n        gx = chainer.functions.matmul(\n            chainer.functions.matmul(- invxT, gy[0]), invxT)\n        return gx,\n\n\nclass BatchInv(function_node.FunctionNode):\n\n    def check_type_forward(self, in_types):\n        type_check.expect(in_types.size() == 1)\n        a_type, = in_types\n        type_check.expect(a_type.dtype == numpy.float32)\n        # Only a minibatch of 2D array shapes allowed\n        type_check.expect(a_type.ndim == 3)\n        # Matrix inversion only allowed for square matrices\n        # so assert the last two dimensions are equal\n        type_check.expect(a_type.shape[-1] == a_type.shape[-2])\n\n    def forward_cpu(self, x):\n        self.retain_outputs((0,))\n        invx = utils.force_array(numpy.linalg.inv(x[0]))\n        return invx,\n\n    def forward_gpu(self, x):\n        self.retain_outputs((0,))\n        invx, info = _inv_gpu(x[0])\n        if chainer.is_debug():\n            if cuda.cupy.any(info != 0):\n                raise ValueError('Input has singular matrices.')\n        return invx,\n\n    def backward(self, x, gy):\n        invx, = self.get_retained_outputs()\n        # Unpack 1-length tuples\n        gy, = gy\n        # Gradient is - x^-T (dx) x^-T\n        ret = chainer.functions.matmul(-invx, gy, transa=True)\n        ret2 = chainer.functions.matmul(ret, invx, transb=True)\n        return ret2,\n\n\ndef inv(a):\n    \"\"\"Computes the inverse of square matrix.\n\n    Args:\n        a (Variable): Input array to compute the inverse for. Shape of\n            the array should be ``(n, n)`` where ``n`` is the dimensionality of\n            a square matrix.\n\n    Returns:\n        ~chainer.Variable: Matrix inverse of ``a``.\n    \"\"\"\n    return Inv().apply((a,))[0]\n\n\ndef batch_inv(a):\n    \"\"\"Computes the inverse of a batch of square matrices.\n\n    Args:\n        a (Variable): Input array to compute the inverse for. Shape of\n            the array should be ``(m, n, n)`` where ``m`` is the number of\n            matrices in the batch, and ``n`` is the dimensionality of a square\n            matrix.\n\n    Returns:\n        ~chainer.Variable: Inverse of every matrix in the batch of matrices.\n    \"\"\"\n    return BatchInv().apply((a,))[0]\n", "path": "chainer/functions/math/inv.py"}]}