PR 681: Add ndtri function, the inverse of the normal distribution function.

This commit is contained in:
Srinivas Vasudevan
2019-08-12 19:26:29 -04:00
parent f59bed7a13
commit e38dd48a27
20 changed files with 498 additions and 70 deletions

View File

@@ -1069,6 +1069,66 @@ void test_gpu_erfc(const Scalar stddev)
gpuFree(d_out);
}
#endif
template <typename Scalar>
void test_gpu_ndtri()
{
Tensor<Scalar, 1> in_x(8);
Tensor<Scalar, 1> out(8);
Tensor<Scalar, 1> expected_out(8);
out.setZero();
in_x(0) = Scalar(1);
in_x(1) = Scalar(0.);
in_x(2) = Scalar(0.5);
in_x(3) = Scalar(0.2);
in_x(4) = Scalar(0.8);
in_x(5) = Scalar(0.9);
in_x(6) = Scalar(0.1);
in_x(7) = Scalar(0.99);
in_x(8) = Scalar(0.01);
expected_out(0) = std::numeric_limits<Scalar>::infinity();
expected_out(1) = -std::numeric_limits<Scalar>::infinity();
expected_out(2) = Scalar(0.0);
expected_out(3) = Scalar(-0.8416212335729142);
expected_out(4) = Scalar(0.8416212335729142);j
expected_out(5) = Scalar(1.2815515655446004);
expected_out(6) = Scalar(-1.2815515655446004);
expected_out(7) = Scalar(2.3263478740408408);
expected_out(8) = Scalar(-2.3263478740408408);
std::size_t bytes = in_x.size() * sizeof(Scalar);
Scalar* d_in_x;
Scalar* d_out;
gpuMalloc((void**)(&d_in_x), bytes);
gpuMalloc((void**)(&d_out), bytes);
gpuMemcpy(d_in_x, in_x.data(), bytes, gpuMemcpyHostToDevice);
Eigen::GpuStreamDevice stream;
Eigen::GpuDevice gpu_device(&stream);
Eigen::TensorMap<Eigen::Tensor<Scalar, 1> > gpu_in_x(d_in_x, 6);
Eigen::TensorMap<Eigen::Tensor<Scalar, 1> > gpu_out(d_out, 6);
gpu_out.device(gpu_device) = gpu_in_x.ndtri();
assert(gpuMemcpyAsync(out.data(), d_out, bytes, gpuMemcpyDeviceToHost, gpu_device.stream()) == gpuSuccess);
assert(gpuStreamSynchronize(gpu_device.stream()) == gpuSuccess);
VERIFY_IS_EQUAL(out(0), expected_out(0));
VERIFY((std::isnan)(out(3)));
for (int i = 1; i < 6; ++i) {
if (i != 3) {
VERIFY_IS_APPROX(out(i), expected_out(i));
}
}
gpuFree(d_in_x);
gpuFree(d_out);
}
template <typename Scalar>
void test_gpu_betainc()
@@ -1538,6 +1598,10 @@ EIGEN_DECLARE_TEST(cxx11_tensor_gpu)
#if !defined(EIGEN_USE_HIP)
// disable these tests on HIP for now.
CALL_SUBTEST_5(test_gpu_ndtri<float>());
CALL_SUBTEST_5(test_gpu_ndtri<double>());
CALL_SUBTEST_5(test_gpu_digamma<float>());
CALL_SUBTEST_5(test_gpu_digamma<double>());

View File

@@ -133,6 +133,26 @@ template<typename ArrayType> void array_special_functions()
}
#endif // EIGEN_HAS_C99_MATH
// Check the ndtri function against scipy.special.ndtri
{
ArrayType x(7), res(7), ref(7);
x << 0.5, 0.2, 0.8, 0.9, 0.1, 0.99, 0.01;
ref << 0., -0.8416212335729142, 0.8416212335729142, 1.2815515655446004, -1.2815515655446004, 2.3263478740408408, -2.3263478740408408;
CALL_SUBTEST( verify_component_wise(ref, ref); );
CALL_SUBTEST( res = x.ndtri(); verify_component_wise(res, ref); );
CALL_SUBTEST( res = ndtri(x); verify_component_wise(res, ref); );
// ndtri(normal_cdf(x)) ~= x
CALL_SUBTEST(
ArrayType m1 = ArrayType::Random(32);
using std::sqrt;
ArrayType cdf_val = (m1 / sqrt(2.)).erf();
cdf_val = (cdf_val + 1.) / 2.;
verify_component_wise(cdf_val.ndtri(), m1););
}
// Check the zeta function against scipy.special.zeta
{
ArrayType x(7), q(7), res(7), ref(7);