From bca303165a67c44f0ca1f213381c78213c4342f6 Mon Sep 17 00:00:00 2001 From: ShenLiang <2282912238@qq.com> Date: Fri, 31 Jul 2020 14:27:38 +0800 Subject: [PATCH] fix inverse bug (#25641) * fix inverse bug, test=develop * fix the untest, test=develop * add singular checking, test=develop * fix the utest, test=develop * use memory::copy, test=develop * fix bost_get, test=develop * fix position, test=develop --- .../fluid/operators/math/matrix_inverse.cu.cc | 11 +++++ .../fluid/tests/unittests/test_inverse_op.py | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/paddle/fluid/operators/math/matrix_inverse.cu.cc b/paddle/fluid/operators/math/matrix_inverse.cu.cc index 8ea4e582ad..614f89a048 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 13cb2b1f8b..3aecbe6e00 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() -- GitLab