mirror of
https://github.com/boostorg/math.git
synced 2026-01-19 04:22:09 +00:00
Fixes: https://github.com/boostorg/math/issues/544. Also fixes uncovered issue in tgamma_ratio where we were previously relying on sub-normals to get the correct result.
203 lines
7.2 KiB
C++
203 lines
7.2 KiB
C++
// (C) Copyright John Maddock 2021.
|
|
// Use, modification and distribution are subject to the
|
|
// Boost Software License, Version 1.0. (See accompanying file
|
|
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
// As detailed in https://github.com/boostorg/math/issues/544
|
|
// our original non-central T test data wasn't accurate past about
|
|
// 20 decimal places. This data generator takes the original input
|
|
// values and generates new CDF and CDF-complement values via
|
|
// tanh_sinh integration - an option that wasn't available when
|
|
// the original values were generated back in 2008.
|
|
//
|
|
// Note that this code will take SEVERAL DAYS to run on typical
|
|
// 2021 hardware.
|
|
//
|
|
|
|
#include <boost/math/special_functions/gamma.hpp>
|
|
#include <boost/math/special_functions/beta.hpp>
|
|
#include <boost/math/special_functions/hypergeometric_1F1.hpp>
|
|
#include <boost/math/constants/constants.hpp>
|
|
#include <boost/math/quadrature/exp_sinh.hpp>
|
|
#include <boost/multiprecision/cpp_bin_float.hpp>
|
|
#include <boost/multiprecision/mpfr.hpp>
|
|
#include <fstream>
|
|
|
|
#include <libs/math/test/table_type.hpp>
|
|
|
|
using namespace boost::math::tools;
|
|
using namespace boost::math;
|
|
using namespace std;
|
|
using namespace boost::multiprecision;
|
|
|
|
//using big_t = number<cpp_bin_float<200, digit_base_10, void, long long>>;
|
|
using big_t = number<mpfr_float_backend<200>>;
|
|
|
|
big_t nct_A(big_t v, big_t x, big_t nu)
|
|
{
|
|
big_t result = boost::math::hypergeometric_1F1(v / 2 + 1, big_t(3) / 2, nu * nu * x * x / (2 * (v + x * x)));
|
|
result *= boost::math::constants::root_two<big_t>() * nu * x;
|
|
result /= (v + x * x) * boost::math::tgamma((v + 1) / 2);
|
|
return result;
|
|
}
|
|
big_t nct_B(big_t v, big_t x, big_t nu)
|
|
{
|
|
big_t result = boost::math::hypergeometric_1F1((v + 1) / 2, big_t(1) / 2, nu * nu * x * x / (2 * (x * x + v)));
|
|
result /= sqrt(v + x * x) * boost::math::tgamma(v / 2 + 1);
|
|
return result;
|
|
}
|
|
big_t nct_PDF(big_t v, big_t nu, big_t x)
|
|
{
|
|
big_t result = nct_A(v, x, nu) + nct_B(v, x, nu);
|
|
result *= pow(v, v / 2) * boost::math::tgamma(v + 1);
|
|
result /= pow(big_t(2), v) * exp(nu * nu / 2) * pow(v + x * x, v / 2) * boost::math::tgamma(v / 2);
|
|
return result;
|
|
}
|
|
|
|
big_t nc_t_F(big_t v, big_t nc, big_t t)
|
|
{
|
|
unsigned j = 0;
|
|
big_t sum = 0;
|
|
big_t tol = std::numeric_limits<big_t>::epsilon();
|
|
do
|
|
{
|
|
big_t x = t * t / (t * t + v);
|
|
big_t p = exp(-nc * nc / 2) * pow(nc * nc / 2, j) / (2 * boost::math::factorial<big_t>(j));
|
|
big_t q = (nc / 2) * exp(-nc * nc / 2) * pow(nc * nc / 2, j) / (boost::math::constants::root_two<big_t>() * boost::math::tgamma(j + big_t(3) / 2));
|
|
big_t term = p * boost::math::ibeta(big_t(j) + 0.5, v / 2, x) + q * boost::math::ibeta(big_t(j + 1), v / 2, x);
|
|
++j;
|
|
|
|
sum += term;
|
|
|
|
if (fabs(sum * tol) > fabs(term))
|
|
break;
|
|
} while (true);
|
|
|
|
return sum + boost::math::constants::half<big_t>() * (1 + boost::math::erf(-nc / boost::math::constants::root_two<big_t>()));
|
|
}
|
|
big_t nc_t(big_t v, big_t nc, big_t t)
|
|
{
|
|
return t < 0 ? 1 - nc_t_F(v, -nc, t) : nc_t_F(v, nc, t);
|
|
}
|
|
|
|
#define SC_(x) BOOST_JOIN(x, f)
|
|
|
|
int main(int, char* [])
|
|
{
|
|
mpfr_set_emax(mpfr_get_emax_max());
|
|
mpfr_set_emin(mpfr_get_emin_min());
|
|
|
|
boost::math::quadrature::exp_sinh<big_t> integrator(10);
|
|
using T = float;
|
|
|
|
#include <libs/math/test/nct.ipp>
|
|
|
|
|
|
for (unsigned i = 0; i < nct.size(); ++i)
|
|
{
|
|
big_t error1, error2;
|
|
big_t v(nct[i][0]), nc(nct[i][1]), x(nct[i][2]);
|
|
big_t cdf, ccdf;
|
|
try{
|
|
cdf = integrator.integrate([&](big_t y) { return nct_PDF(v, nc, y); }, -std::numeric_limits<big_t>::infinity(), x, big_t(1e-36), &error1);
|
|
ccdf = integrator.integrate([&](big_t y) { return nct_PDF(v, nc, y); }, x, std::numeric_limits<big_t>::infinity(), big_t(1e-36), &error2);
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
std::cout << "// " << e.what() << " reverting to ibeta method" << std::endl;
|
|
error1 = error2 = 0;
|
|
cdf = nc_t(v, nc, x);
|
|
ccdf = 1 - cdf;
|
|
}
|
|
|
|
if (error1 > 1e-35)
|
|
{
|
|
std::cout << "// Accuracy for cdf was " << error1 << " reverting to ibeta method" << std::endl;
|
|
cdf = nc_t(v, nc, x);
|
|
}
|
|
if (error2 > 1e-35)
|
|
{
|
|
std::cout << "// Accuracy for complement cdf was " << error2 << " reverting to ibeta method" << std::endl;
|
|
ccdf = 1 - nc_t(v, nc, x);
|
|
}
|
|
|
|
std::cout << std::setprecision(40);
|
|
std::cout << "{{ SC_(" << nct[i][0] << "), SC_(" << nct[i][1] << "), SC_(" << nct[i][2] << "), SC_(";
|
|
std::cout << cdf << "), SC_(" << ccdf << ") }}," << std::endl;
|
|
}
|
|
|
|
#include <libs/math/test/nct_small_delta.ipp>
|
|
for (unsigned i = 0; i < nct_small_delta.size(); ++i)
|
|
{
|
|
big_t error1, error2;
|
|
big_t v(nct_small_delta[i][0]), nc(nct_small_delta[i][1]), x(nct_small_delta[i][2]);
|
|
big_t cdf, ccdf;
|
|
try {
|
|
cdf = integrator.integrate([&](big_t y) { return nct_PDF(v, nc, y); }, -std::numeric_limits<big_t>::infinity(), x, big_t(1e-36), &error1);
|
|
ccdf = integrator.integrate([&](big_t y) { return nct_PDF(v, nc, y); }, x, std::numeric_limits<big_t>::infinity(), big_t(1e-36), &error2);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cout << "// " << e.what() << " reverting to ibeta method" << std::endl;
|
|
error1 = error2 = 0;
|
|
cdf = nc_t(v, nc, x);
|
|
ccdf = 1 - cdf;
|
|
}
|
|
|
|
if (error1 > 1e-35)
|
|
{
|
|
std::cout << "// Accuracy for cdf was " << error1 << " reverting to ibeta method" << std::endl;
|
|
cdf = nc_t(v, nc, x);
|
|
}
|
|
if (error2 > 1e-35)
|
|
{
|
|
std::cout << "// Accuracy for complement cdf was " << error2 << " reverting to ibeta method" << std::endl;
|
|
ccdf = 1 - nc_t(v, nc, x);
|
|
}
|
|
|
|
std::cout << std::setprecision(40);
|
|
std::cout << "{{ SC_(" << v << "), SC_(" << nc << "), SC_(" << x << "), SC_(";
|
|
std::cout << cdf << "), SC_(" << ccdf << ") }}," << std::endl;
|
|
}
|
|
|
|
#include "../test/nct_asym.ipp"
|
|
for (unsigned i = 0; i < nct_asym.size(); ++i)
|
|
{
|
|
big_t error1, error2;
|
|
big_t v(nct_asym[i][0]), nc(nct_asym[i][1]), x(nct_asym[i][2]);
|
|
big_t cdf, ccdf;
|
|
try {
|
|
cdf = integrator.integrate([&](big_t y) { return nct_PDF(v, nc, y); }, -std::numeric_limits<big_t>::infinity(), x, big_t(1e-36), &error1);
|
|
ccdf = integrator.integrate([&](big_t y) { return nct_PDF(v, nc, y); }, x, std::numeric_limits<big_t>::infinity(), big_t(1e-36), &error2);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cout << "// " << e.what() << " reverting to ibeta method" << std::endl;
|
|
error1 = error2 = 0;
|
|
cdf = nc_t(v, nc, x);
|
|
ccdf = 1 - cdf;
|
|
}
|
|
|
|
if (error1 > 1e-35)
|
|
{
|
|
std::cout << "// Accuracy for cdf was " << error1 << " reverting to ibeta method" << std::endl;
|
|
cdf = nc_t(v, nc, x);
|
|
}
|
|
if (error2 > 1e-35)
|
|
{
|
|
std::cout << "// Accuracy for complement cdf was " << error2 << " reverting to ibeta method" << std::endl;
|
|
ccdf = 1 - nc_t(v, nc, x);
|
|
}
|
|
|
|
std::cout << std::setprecision(40);
|
|
std::cout << "{{ SC_(" << v << "), SC_(" << nc << "), SC_(" << x << "), SC_(";
|
|
std::cout << cdf << "), SC_(" << ccdf << ") }}," << std::endl;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|