diff --git a/paddle/fluid/operators/math/matrix_inverse.cu.cc b/paddle/fluid/operators/math/matrix_inverse.cu.cc index 8ea4e582ad10c3220b7a27986ec88005e5198b5c..614f89a048c4e92e758ddb39da43322be284f9e5 100644 --- a/paddle/fluid/operators/math/matrix_inverse.cu.cc +++ b/paddle/fluid/operators/math/matrix_inverse.cu.cc @@ -67,6 +67,8 @@ class MatrixInverseFunctor { auto blas = math::GetBlas(context); + std::vector info; // only for singular checking + info.resize(batch_size); // This functions in cuBLAS is intended to be used for matrices of small // sizes where the launch overhead is a significant factor. // TODO(Xreki): call function in cusolver for large matrices. @@ -91,6 +93,15 @@ class MatrixInverseFunctor { reinterpret_cast(tmp_gpu_ptrs_data->ptr()), gpu_pivot_ptr, gpu_inv_ptrs, gpu_info_ptr, batch_size); } + memory::Copy(platform::CPUPlace(), info.data(), + BOOST_GET_CONST(platform::CUDAPlace, context.GetPlace()), + gpu_info_ptr, sizeof(int) * batch_size, context.stream()); + for (int i = 0; i < batch_size; ++i) { + PADDLE_ENFORCE_EQ(info[i], 0, + platform::errors::PreconditionNotMet( + "For batch [%d]: U(%d, %d) is zero, singular U.", i, + info[i], info[i])); + } } }; diff --git a/python/paddle/fluid/tests/unittests/test_inverse_op.py b/python/paddle/fluid/tests/unittests/test_inverse_op.py index 13cb2b1f8b1161cd35cc75dd4589de62196c4525..3aecbe6e00b3b06e0e4f764314691103c561467a 100644 --- a/python/paddle/fluid/tests/unittests/test_inverse_op.py +++ b/python/paddle/fluid/tests/unittests/test_inverse_op.py @@ -140,5 +140,47 @@ class TestInverseAPIError(unittest.TestCase): self.assertRaises(ValueError, paddle.inverse, input) +class TestInverseSingularAPI(unittest.TestCase): + def setUp(self): + self.places = [fluid.CPUPlace()] + if core.is_compiled_with_cuda(): + self.places.append(fluid.CUDAPlace(0)) + + def check_static_result(self, place, with_out=False): + with fluid.program_guard(fluid.Program(), fluid.Program()): + input = fluid.data(name="input", shape=[4, 4], dtype="float64") + if with_out: + out = fluid.data(name="output", shape=[4, 4], dtype="float64") + else: + out = None + result = paddle.inverse(input=input, out=out) + + input_np = np.zeros([4, 4]).astype("float64") + + exe = fluid.Executor(place) + try: + fetches = exe.run(fluid.default_main_program(), + feed={"input": input_np}, + fetch_list=[result]) + except fluid.core.EnforceNotMet as ex: + print("The mat is singular") + pass + + def test_static(self): + for place in self.places: + self.check_static_result(place=place) + + def test_dygraph(self): + for place in self.places: + with fluid.dygraph.guard(place): + input_np = np.ones([4, 4]).astype("float64") + input = fluid.dygraph.to_variable(input_np) + try: + result = paddle.inverse(input) + except fluid.core.EnforceNotMet as ex: + print("The mat is singular") + pass + + if __name__ == "__main__": unittest.main()