mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-30 20:40:28 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			328 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //  (C) Copyright John Maddock 2006.
 | |
| //  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)
 | |
| 
 | |
| #include <pch.hpp>
 | |
| 
 | |
| #define BOOST_TEST_MAIN
 | |
| #include <boost/test/unit_test.hpp>
 | |
| #include <boost/test/floating_point_comparison.hpp>
 | |
| #include <boost/test/results_collector.hpp>
 | |
| #include <boost/math/special_functions/beta.hpp>
 | |
| #include <boost/math/tools/roots.hpp>
 | |
| #include <boost/test/results_collector.hpp>
 | |
| #include <boost/test/unit_test.hpp>
 | |
| #include <boost/array.hpp>
 | |
| #include "table_type.hpp"
 | |
| #include <iostream>
 | |
| #include <iomanip>
 | |
| 
 | |
| #define BOOST_CHECK_CLOSE_EX(a, b, prec, i) \
 | |
|    {\
 | |
|       unsigned int failures = boost::unit_test::results_collector.results( boost::unit_test::framework::current_test_case().p_id ).p_assertions_failed;\
 | |
|       BOOST_CHECK_CLOSE(a, b, prec); \
 | |
|       if(failures != boost::unit_test::results_collector.results( boost::unit_test::framework::current_test_case().p_id ).p_assertions_failed)\
 | |
|       {\
 | |
|          std::cerr << "Failure was at row " << i << std::endl;\
 | |
|          std::cerr << std::setprecision(35); \
 | |
|          std::cerr << "{ " << data[i][0] << " , " << data[i][1] << " , " << data[i][2];\
 | |
|          std::cerr << " , " << data[i][3] << " , " << data[i][4] << " , " << data[i][5] << " } " << std::endl;\
 | |
|       }\
 | |
|    }
 | |
| 
 | |
| 
 | |
| //
 | |
| // Implement various versions of inverse of the incomplete beta
 | |
| // using different root finding algorithms, and deliberately "bad"
 | |
| // starting conditions: that way we get all the pathological cases
 | |
| // we could ever wish for!!!
 | |
| //
 | |
| 
 | |
| template <class T, class Policy>
 | |
| struct ibeta_roots_1   // for first order algorithms
 | |
| {
 | |
|    ibeta_roots_1(T _a, T _b, T t, bool inv = false)
 | |
|       : a(_a), b(_b), target(t), invert(inv) {}
 | |
| 
 | |
|    T operator()(const T& x)
 | |
|    {
 | |
|       return boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
 | |
|    }
 | |
| private:
 | |
|    T a, b, target;
 | |
|    bool invert;
 | |
| };
 | |
| 
 | |
| template <class T, class Policy>
 | |
| struct ibeta_roots_2   // for second order algorithms
 | |
| {
 | |
|    ibeta_roots_2(T _a, T _b, T t, bool inv = false)
 | |
|       : a(_a), b(_b), target(t), invert(inv) {}
 | |
| 
 | |
|    boost::math::tuple<T, T> operator()(const T& x)
 | |
|    {
 | |
|       typedef typename boost::math::lanczos::lanczos<T, Policy>::type L;
 | |
|       T f = boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
 | |
|       T f1 = invert ?
 | |
|          -boost::math::detail::ibeta_power_terms(b, a, 1 - x, x, L(), true, Policy())
 | |
|                : boost::math::detail::ibeta_power_terms(a, b, x, 1 - x, L(), true, Policy());
 | |
|       T y = 1 - x;
 | |
|       if(y == 0)
 | |
|          y = boost::math::tools::min_value<T>() * 8;
 | |
|       f1 /= y * x;
 | |
| 
 | |
|       // make sure we don't have a zero derivative:
 | |
|       if(f1 == 0)
 | |
|          f1 = (invert ? -1 : 1) * boost::math::tools::min_value<T>() * 64;
 | |
| 
 | |
|       return boost::math::make_tuple(f, f1);
 | |
|    }
 | |
| private:
 | |
|    T a, b, target;
 | |
|    bool invert;
 | |
| };
 | |
| 
 | |
| template <class T, class Policy>
 | |
| struct ibeta_roots_3   // for third order algorithms
 | |
| {
 | |
|    ibeta_roots_3(T _a, T _b, T t, bool inv = false)
 | |
|       : a(_a), b(_b), target(t), invert(inv) {}
 | |
| 
 | |
|    boost::math::tuple<T, T, T> operator()(const T& x)
 | |
|    {
 | |
|       typedef typename boost::math::lanczos::lanczos<T, Policy>::type L;
 | |
|       T f = boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
 | |
|       T f1 = invert ?
 | |
|                -boost::math::detail::ibeta_power_terms(b, a, 1 - x, x, L(), true, Policy())
 | |
|                : boost::math::detail::ibeta_power_terms(a, b, x, 1 - x, L(), true, Policy());
 | |
|       T y = 1 - x;
 | |
|       if(y == 0)
 | |
|          y = boost::math::tools::min_value<T>() * 8;
 | |
|       f1 /= y * x;
 | |
|       T f2 = f1 * (-y * a + (b - 2) * x + 1) / (y * x);
 | |
|       if(invert)
 | |
|          f2 = -f2;
 | |
| 
 | |
|       // make sure we don't have a zero derivative:
 | |
|       if(f1 == 0)
 | |
|          f1 = (invert ? -1 : 1) * boost::math::tools::min_value<T>() * 64;
 | |
| 
 | |
|       return boost::math::make_tuple(f, f1, f2);
 | |
|    }
 | |
| private:
 | |
|    T a, b, target;
 | |
|    bool invert;
 | |
| };
 | |
| 
 | |
| double inverse_ibeta_bisect(double a, double b, double z)
 | |
| {
 | |
|    typedef boost::math::policies::policy<> pol;
 | |
|    bool invert = false;
 | |
|    int bits = std::numeric_limits<double>::digits;
 | |
| 
 | |
|    //
 | |
|    // special cases, we need to have these because there may be other
 | |
|    // possible answers:
 | |
|    //
 | |
|    if(z == 1) return 1;
 | |
|    if(z == 0) return 0;
 | |
| 
 | |
|    //
 | |
|    // We need a good estimate of the error in the incomplete beta function
 | |
|    // so that we don't set the desired precision too high.  Assume that 3-bits
 | |
|    // are lost each time the arguments increase by a factor of 10:
 | |
|    //
 | |
|    using namespace std;
 | |
|    int bits_lost = static_cast<int>(ceil(log10((std::max)(a, b)) * 3));
 | |
|    if(bits_lost < 0)
 | |
|       bits_lost = 3;
 | |
|    else
 | |
|       bits_lost += 3;
 | |
|    int precision = bits - bits_lost;
 | |
| 
 | |
|    double min = 0;
 | |
|    double max = 1;
 | |
|    boost::math::tools::eps_tolerance<double> tol(precision);
 | |
|    return boost::math::tools::bisect(ibeta_roots_1<double, pol>(a, b, z, invert), min, max, tol).first;
 | |
| }
 | |
| 
 | |
| double inverse_ibeta_newton(double a, double b, double z)
 | |
| {
 | |
|    double guess = 0.5;
 | |
|    bool invert = false;
 | |
|    int bits = std::numeric_limits<double>::digits;
 | |
| 
 | |
|    //
 | |
|    // special cases, we need to have these because there may be other
 | |
|    // possible answers:
 | |
|    //
 | |
|    if(z == 1) return 1;
 | |
|    if(z == 0) return 0;
 | |
| 
 | |
|    //
 | |
|    // We need a good estimate of the error in the incomplete beta function
 | |
|    // so that we don't set the desired precision too high.  Assume that 3-bits
 | |
|    // are lost each time the arguments increase by a factor of 10:
 | |
|    //
 | |
|    using namespace std;
 | |
|    int bits_lost = static_cast<int>(ceil(log10((std::max)(a, b)) * 3));
 | |
|    if(bits_lost < 0)
 | |
|       bits_lost = 3;
 | |
|    else
 | |
|       bits_lost += 3;
 | |
|    int precision = bits - bits_lost;
 | |
| 
 | |
|    double min = 0;
 | |
|    double max = 1;
 | |
|    return boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(a, b, z, invert), guess, min, max, precision);
 | |
| }
 | |
| 
 | |
| double inverse_ibeta_halley(double a, double b, double z)
 | |
| {
 | |
|    double guess = 0.5;
 | |
|    bool invert = false;
 | |
|    int bits = std::numeric_limits<double>::digits;
 | |
| 
 | |
|    //
 | |
|    // special cases, we need to have these because there may be other
 | |
|    // possible answers:
 | |
|    //
 | |
|    if(z == 1) return 1;
 | |
|    if(z == 0) return 0;
 | |
| 
 | |
|    //
 | |
|    // We need a good estimate of the error in the incomplete beta function
 | |
|    // so that we don't set the desired precision too high.  Assume that 3-bits
 | |
|    // are lost each time the arguments increase by a factor of 10:
 | |
|    //
 | |
|    using namespace std;
 | |
|    int bits_lost = static_cast<int>(ceil(log10((std::max)(a, b)) * 3));
 | |
|    if(bits_lost < 0)
 | |
|       bits_lost = 3;
 | |
|    else
 | |
|       bits_lost += 3;
 | |
|    int precision = bits - bits_lost;
 | |
| 
 | |
|    double min = 0;
 | |
|    double max = 1;
 | |
|    return boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(a, b, z, invert), guess, min, max, precision);
 | |
| }
 | |
| 
 | |
| double inverse_ibeta_schroder(double a, double b, double z)
 | |
| {
 | |
|    double guess = 0.5;
 | |
|    bool invert = false;
 | |
|    int bits = std::numeric_limits<double>::digits;
 | |
| 
 | |
|    //
 | |
|    // special cases, we need to have these because there may be other
 | |
|    // possible answers:
 | |
|    //
 | |
|    if(z == 1) return 1;
 | |
|    if(z == 0) return 0;
 | |
| 
 | |
|    //
 | |
|    // We need a good estimate of the error in the incomplete beta function
 | |
|    // so that we don't set the desired precision too high.  Assume that 3-bits
 | |
|    // are lost each time the arguments increase by a factor of 10:
 | |
|    //
 | |
|    using namespace std;
 | |
|    int bits_lost = static_cast<int>(ceil(log10((std::max)(a, b)) * 3));
 | |
|    if(bits_lost < 0)
 | |
|       bits_lost = 3;
 | |
|    else
 | |
|       bits_lost += 3;
 | |
|    int precision = bits - bits_lost;
 | |
| 
 | |
|    double min = 0;
 | |
|    double max = 1;
 | |
|    return boost::math::tools::schroder_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(a, b, z, invert), guess, min, max, precision);
 | |
| }
 | |
| 
 | |
| 
 | |
| template <class Real, class T>
 | |
| void test_inverses(const T& data)
 | |
| {
 | |
|    using namespace std;
 | |
|    typedef typename T::value_type row_type;
 | |
|    typedef Real                   value_type;
 | |
| 
 | |
|    value_type precision = static_cast<value_type>(ldexp(1.0, 1-boost::math::policies::digits<value_type, boost::math::policies::policy<> >()/2)) * 150;
 | |
|    if(boost::math::policies::digits<value_type, boost::math::policies::policy<> >() < 50)
 | |
|       precision = 1;   // 1% or two decimal digits, all we can hope for when the input is truncated
 | |
| 
 | |
|    for(unsigned i = 0; i < data.size(); ++i)
 | |
|    {
 | |
|       //
 | |
|       // These inverse tests are thrown off if the output of the
 | |
|       // incomplete beta is too close to 1: basically there is insuffient
 | |
|       // information left in the value we're using as input to the inverse
 | |
|       // to be able to get back to the original value.
 | |
|       //
 | |
|       if(data[i][5] == 0)
 | |
|       {
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_halley(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(0));
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_schroder(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(0));
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_newton(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(0));
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_bisect(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(0));
 | |
|       }
 | |
|       else if((1 - data[i][5] > 0.001) 
 | |
|          && (fabs(data[i][5]) > 2 * boost::math::tools::min_value<value_type>()) 
 | |
|          && (fabs(data[i][5]) > 2 * boost::math::tools::min_value<double>()))
 | |
|       {
 | |
|          value_type inv = inverse_ibeta_halley(Real(data[i][0]), Real(data[i][1]), Real(data[i][5]));
 | |
|          BOOST_CHECK_CLOSE_EX(Real(data[i][2]), inv, precision, i);
 | |
|          inv = inverse_ibeta_schroder(Real(data[i][0]), Real(data[i][1]), Real(data[i][5]));
 | |
|          BOOST_CHECK_CLOSE_EX(Real(data[i][2]), inv, precision, i);
 | |
|          inv = inverse_ibeta_newton(Real(data[i][0]), Real(data[i][1]), Real(data[i][5]));
 | |
|          BOOST_CHECK_CLOSE_EX(Real(data[i][2]), inv, precision, i);
 | |
|          inv = inverse_ibeta_bisect(Real(data[i][0]), Real(data[i][1]), Real(data[i][5]));
 | |
|          BOOST_CHECK_CLOSE_EX(Real(data[i][2]), inv, precision, i);
 | |
|       }
 | |
|       else if(1 == data[i][5])
 | |
|       {
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_halley(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(1));
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_schroder(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(1));
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_newton(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(1));
 | |
|          BOOST_CHECK_EQUAL(inverse_ibeta_bisect(Real(data[i][0]), Real(data[i][1]), Real(data[i][5])), value_type(1));
 | |
|       }
 | |
| 
 | |
|    }
 | |
| }
 | |
| 
 | |
| #ifndef SC_
 | |
| #define SC_(x) static_cast<typename table_type<T>::type>(BOOST_JOIN(x, L))
 | |
| #endif
 | |
| 
 | |
| template <class T>
 | |
| void test_beta(T, const char* /* name */)
 | |
| {
 | |
|    //
 | |
|    // The actual test data is rather verbose, so it's in a separate file
 | |
|    //
 | |
|    // The contents are as follows, each row of data contains
 | |
|    // five items, input value a, input value b, integration limits x, beta(a, b, x) and ibeta(a, b, x):
 | |
|    //
 | |
| #  include "ibeta_small_data.ipp"
 | |
| 
 | |
|    test_inverses<T>(ibeta_small_data);
 | |
| 
 | |
| #  include "ibeta_data.ipp"
 | |
| 
 | |
|    test_inverses<T>(ibeta_data);
 | |
| 
 | |
| #  include "ibeta_large_data.ipp"
 | |
| 
 | |
|    test_inverses<T>(ibeta_large_data);
 | |
| }
 | |
| 
 | |
| BOOST_AUTO_TEST_CASE( test_main )
 | |
| {
 | |
|    test_beta(0.1, "double");
 | |
|    
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 |