2
0
mirror of https://github.com/boostorg/math.git synced 2026-01-19 16:32:10 +00:00
Files
math/tools/nc_t_data.cpp
jzmaddock 65c8cb24fa Fix up non-central T test data for 128-bit floats.
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.
2021-03-01 12:05:22 +00:00

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;
}