prompt: 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()
  
  ```
  
golden_diff: 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):
  
verification_info: {"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"}]}