From e367d3c8eade36c63f5b4f1c7321aa5496992c22 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 15 Feb 2022 17:57:18 +0100 Subject: [PATCH 01/60] Arcsine distribution logpdf --- include/boost/math/distributions/arcsine.hpp | 28 +++++++++++++++++++ .../detail/derived_accessors.hpp | 6 ++++ 2 files changed, 34 insertions(+) diff --git a/include/boost/math/distributions/arcsine.hpp b/include/boost/math/distributions/arcsine.hpp index 5cb2c05f9b..36cddf27c7 100644 --- a/include/boost/math/distributions/arcsine.hpp +++ b/include/boost/math/distributions/arcsine.hpp @@ -366,6 +366,34 @@ namespace boost return result; } // pdf + template + inline RealType logpdf(const arcsine_distribution& dist, const RealType& xx) + { + BOOST_FPU_EXCEPTION_GUARD + BOOST_MATH_STD_USING // For ADL of std functions. + + static const char* function = "boost::math::logpdf(arcsine_distribution<%1%> const&, %1%)"; + + RealType lo = dist.x_min(); + RealType hi = dist.x_max(); + RealType x = xx; + + // Argument checks: + RealType result = 0; + if (false == arcsine_detail::check_dist_and_x( + function, + lo, hi, x, + &result, Policy())) + { + return result; + } + + using boost::math::constants::pi; + using boost::math::constants::half; + result = -half() * log(-(x - 1) * x) - log(pi()); + return result; + } // logpdf + template inline RealType cdf(const arcsine_distribution& dist, const RealType& x) { // Cumulative Distribution Function arcsine. diff --git a/include/boost/math/distributions/detail/derived_accessors.hpp b/include/boost/math/distributions/detail/derived_accessors.hpp index 9101c8ae1f..c6c24977da 100644 --- a/include/boost/math/distributions/detail/derived_accessors.hpp +++ b/include/boost/math/distributions/detail/derived_accessors.hpp @@ -110,6 +110,12 @@ inline typename Distribution::value_type pdf(const Distribution& dist, const Rea return pdf(dist, static_cast(x)); } template +inline typename Distribution::value_type logpdf(const Distribution& dist, const RealType& x) +{ + typedef typename Distribution::value_type value_type; + return logpdf(dist, static_cast(x)); +} +template inline typename Distribution::value_type cdf(const Distribution& dist, const RealType& x) { typedef typename Distribution::value_type value_type; From 2512a1330148e7431e46189109d4c7313c08076c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 15 Feb 2022 19:14:57 +0100 Subject: [PATCH 02/60] Add tests for arcsine logpdf and fix definition [ci skip] --- include/boost/math/distributions/arcsine.hpp | 2 +- test/test_arcsine.cpp | 53 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/include/boost/math/distributions/arcsine.hpp b/include/boost/math/distributions/arcsine.hpp index 36cddf27c7..cc3ba10832 100644 --- a/include/boost/math/distributions/arcsine.hpp +++ b/include/boost/math/distributions/arcsine.hpp @@ -390,7 +390,7 @@ namespace boost using boost::math::constants::pi; using boost::math::constants::half; - result = -half() * log(-(x - 1) * x) - log(pi()); + result = -half() * log((hi - x)*(x - lo)) - log(pi()); return result; } // logpdf diff --git a/test/test_arcsine.cpp b/test/test_arcsine.cpp index 68036686e5..0c2d847a96 100644 --- a/test/test_arcsine.cpp +++ b/test/test_arcsine.cpp @@ -105,6 +105,14 @@ void test_ignore_policy(RealType) BOOST_CHECK((boost::math::isnan)(pdf(ignore_error_arcsine(0, 1), static_cast (+2)))); // x > x_max BOOST_CHECK((boost::math::isnan)(pdf(ignore_error_arcsine(-1, 1), static_cast (+2)))); // x > x_max + // Logpdf + BOOST_CHECK((boost::math::isnan)(logpdf(ignore_error_arcsine(0, 1), std::numeric_limits::infinity()))); // x == infinity + BOOST_CHECK((boost::math::isnan)(logpdf(ignore_error_arcsine(-1, 1), std::numeric_limits::infinity()))); // x == infinity + BOOST_CHECK((boost::math::isnan)(logpdf(ignore_error_arcsine(0, 1), static_cast (-2)))); // x < xmin + BOOST_CHECK((boost::math::isnan)(logpdf(ignore_error_arcsine(-1, 1), static_cast (-2)))); // x < xmin + BOOST_CHECK((boost::math::isnan)(logpdf(ignore_error_arcsine(0, 1), static_cast (+2)))); // x > x_max + BOOST_CHECK((boost::math::isnan)(logpdf(ignore_error_arcsine(-1, 1), static_cast (+2)))); // x > x_max + // Mean BOOST_CHECK((boost::math::isnan)(mean(ignore_error_arcsine(-nan, 0)))); BOOST_CHECK((boost::math::isnan)(mean(ignore_error_arcsine(+nan, 0)))); @@ -239,6 +247,7 @@ void test_spots(RealType) using boost::math::arcsine_distribution; using ::boost::math::cdf; using ::boost::math::pdf; + using ::boost::math::logpdf; using ::boost::math::complement; using ::boost::math::quantile; @@ -292,6 +301,16 @@ void test_spots(RealType) BOOST_CHECK_CLOSE_FRACTION(pdf(arcsine_01, static_cast(1) - tolerance), 1 /(sqrt(tolerance) * boost::math::constants::pi()), 2 * tolerance); // + // Log PDF + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.000001), static_cast(5.7630258931329868780772138043668005779060097243996L), tolerance); + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.000005), static_cast(4.9583089369219367114435788047327747268154560240604L), tolerance); + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.05), static_cast(0.37878289812137058928728250884555529541061717942415L), tolerance); + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.5), static_cast(-0.45158270528945486472619522989488214357179467855506L), tolerance); + // Note loss of significance when x is near x_max. + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.95), static_cast(0.37878289812137058928728250884555529541061717942415L), 8 * tolerance); // Less accurate. + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.999995), static_cast(4.9583089369219367114435788047327747268154560240604L), 50000 * tolerance); // Much less accurate. + BOOST_CHECK_CLOSE_FRACTION(logpdf(arcsine_01, 0.999999), static_cast(5.7630258931329868780772138043668005779060097243996L), 100000 * tolerance);// Even less accurate. + // CDF BOOST_CHECK_CLOSE_FRACTION(cdf(arcsine_01, 0.000001), static_cast(0.00063661987847092448418377367957384866092127786060574L), tolerance); BOOST_CHECK_CLOSE_FRACTION(cdf(arcsine_01, 0.000005), static_cast(0.0014235262731079289297302426454125318201831474507326L), tolerance); @@ -353,6 +372,10 @@ void test_spots(RealType) BOOST_CHECK_CLOSE_FRACTION(pdf(as_m11, 0.5), static_cast(0.36755259694786136634088433220864629426492432024443L), tolerance); BOOST_CHECK_CLOSE_FRACTION(pdf(as_m11, 0.95), static_cast(1.0194074882503562519812229448639426942621591013381L), 2 * tolerance); // Less accurate. + BOOST_CHECK_CLOSE_FRACTION(logpdf(as_m11, 0.05), static_cast(-1.1434783207403409089630164813372974217316704642782L), tolerance); + BOOST_CHECK_CLOSE_FRACTION(logpdf(as_m11, 0.5), static_cast(-1.0008888496235097104238178483561449958955399574664L), tolerance); + BOOST_CHECK_CLOSE_FRACTION(logpdf(as_m11, 0.95), static_cast(0.019221564639767605567429885545559909302927558782238L), 100 * tolerance); // Less accurate. + BOOST_CHECK_CLOSE_FRACTION(cdf(as_m11, 0.05), static_cast(0.51592213323666034437274347433261364289389772737836L), tolerance); BOOST_CHECK_CLOSE_FRACTION(cdf(as_m11, 0.5), static_cast(0.66666666666666666666666666666666666666666666666667L), 2 * tolerance); BOOST_CHECK_CLOSE_FRACTION(cdf(as_m11, 0.95), static_cast(0.89891737589574013042121018491729701360300248368629L), tolerance); // Not less accurate. @@ -445,6 +468,31 @@ void test_spots(RealType) arcsine_distribution(static_cast(0), static_cast(1)), // bad x > 1. static_cast(999)), std::domain_error); + BOOST_MATH_CHECK_THROW( // For various bad arguments. + logpdf( + arcsine_distribution(static_cast(+1), static_cast(-1)), // min_x > max_x + static_cast(1)), std::domain_error); + + BOOST_MATH_CHECK_THROW( + logpdf( + arcsine_distribution(static_cast(1), static_cast(0)), // bad constructor parameters. + static_cast(1)), std::domain_error); + + BOOST_MATH_CHECK_THROW( + logpdf( + arcsine_distribution(static_cast(1), static_cast(-1)), // bad constructor parameters. + static_cast(1)), std::domain_error); + + BOOST_MATH_CHECK_THROW( + logpdf( + arcsine_distribution(static_cast(1), static_cast(1)), // equal constructor parameters. + static_cast(-1)), std::domain_error); + + BOOST_MATH_CHECK_THROW( + logpdf( + arcsine_distribution(static_cast(0), static_cast(1)), // bad x > 1. + static_cast(999)), std::domain_error); + // Checks on things that are errors. // Construction with 'bad' parameters. @@ -453,6 +501,7 @@ void test_spots(RealType) arcsine_distribution<> dist; BOOST_MATH_CHECK_THROW(pdf(dist, -1), std::domain_error); + BOOST_MATH_CHECK_THROW(logpdf(dist, -1), std::domain_error); BOOST_MATH_CHECK_THROW(cdf(dist, -1), std::domain_error); BOOST_MATH_CHECK_THROW(cdf(complement(dist, -1)), std::domain_error); BOOST_MATH_CHECK_THROW(quantile(dist, -1), std::domain_error); @@ -463,6 +512,8 @@ void test_spots(RealType) // Various combinations of bad constructor and member function parameters. BOOST_MATH_CHECK_THROW(pdf(boost::math::arcsine_distribution(0, 1), -1), std::domain_error); BOOST_MATH_CHECK_THROW(pdf(boost::math::arcsine_distribution(-1, 1), +2), std::domain_error); + BOOST_MATH_CHECK_THROW(logpdf(boost::math::arcsine_distribution(0, 1), -1), std::domain_error); + BOOST_MATH_CHECK_THROW(logpdf(boost::math::arcsine_distribution(-1, 1), +2), std::domain_error); BOOST_MATH_CHECK_THROW(quantile(boost::math::arcsine_distribution(1, 1), -1), std::domain_error); BOOST_MATH_CHECK_THROW(quantile(boost::math::arcsine_distribution(1, 1), 2), std::domain_error); @@ -484,6 +535,7 @@ void test_spots(RealType) arcsine_distribution w(RealType(-1), RealType(+1)); // NaN parameters to member functions should throw. BOOST_MATH_CHECK_THROW(pdf(w, +nan), std::domain_error); // x = NaN + BOOST_MATH_CHECK_THROW(logpdf(w, +nan), std::domain_error); // x = NaN BOOST_MATH_CHECK_THROW(cdf(w, +nan), std::domain_error); // x = NaN BOOST_MATH_CHECK_THROW(cdf(complement(w, +nan)), std::domain_error); // x = + nan BOOST_MATH_CHECK_THROW(quantile(w, +nan), std::domain_error); // p = + nan @@ -511,6 +563,7 @@ void test_spots(RealType) BOOST_MATH_CHECK_THROW(arcsine_distribution(1, inf), std::domain_error); #endif BOOST_MATH_CHECK_THROW(pdf(w, +inf), std::domain_error); // x = inf + BOOST_MATH_CHECK_THROW(logpdf(w, +inf), std::domain_error); // x = inf BOOST_MATH_CHECK_THROW(cdf(w, +inf), std::domain_error); // x = inf BOOST_MATH_CHECK_THROW(cdf(complement(w, +inf)), std::domain_error); // x = + inf BOOST_MATH_CHECK_THROW(quantile(w, +inf), std::domain_error); // p = + inf From d739e1d2e8a8a26fdeaa046f63cfb659051073b5 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 16 Feb 2022 13:54:53 +0100 Subject: [PATCH 03/60] Remove arcsine logpdf specialization and add default implementation --- include/boost/math/distributions/arcsine.hpp | 29 +------------------ .../detail/derived_accessors.hpp | 3 +- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/include/boost/math/distributions/arcsine.hpp b/include/boost/math/distributions/arcsine.hpp index cc3ba10832..a8fcbbc05f 100644 --- a/include/boost/math/distributions/arcsine.hpp +++ b/include/boost/math/distributions/arcsine.hpp @@ -29,6 +29,7 @@ #ifndef BOOST_MATH_DIST_ARCSINE_HPP #define BOOST_MATH_DIST_ARCSINE_HPP +#include #include #include // complements. #include // error checks. @@ -366,34 +367,6 @@ namespace boost return result; } // pdf - template - inline RealType logpdf(const arcsine_distribution& dist, const RealType& xx) - { - BOOST_FPU_EXCEPTION_GUARD - BOOST_MATH_STD_USING // For ADL of std functions. - - static const char* function = "boost::math::logpdf(arcsine_distribution<%1%> const&, %1%)"; - - RealType lo = dist.x_min(); - RealType hi = dist.x_max(); - RealType x = xx; - - // Argument checks: - RealType result = 0; - if (false == arcsine_detail::check_dist_and_x( - function, - lo, hi, x, - &result, Policy())) - { - return result; - } - - using boost::math::constants::pi; - using boost::math::constants::half; - result = -half() * log((hi - x)*(x - lo)) - log(pi()); - return result; - } // logpdf - template inline RealType cdf(const arcsine_distribution& dist, const RealType& x) { // Cumulative Distribution Function arcsine. diff --git a/include/boost/math/distributions/detail/derived_accessors.hpp b/include/boost/math/distributions/detail/derived_accessors.hpp index c6c24977da..e2eca511bf 100644 --- a/include/boost/math/distributions/detail/derived_accessors.hpp +++ b/include/boost/math/distributions/detail/derived_accessors.hpp @@ -112,8 +112,9 @@ inline typename Distribution::value_type pdf(const Distribution& dist, const Rea template inline typename Distribution::value_type logpdf(const Distribution& dist, const RealType& x) { + using std::log; typedef typename Distribution::value_type value_type; - return logpdf(dist, static_cast(x)); + return log(pdf(dist, static_cast(x))); } template inline typename Distribution::value_type cdf(const Distribution& dist, const RealType& x) From 18c33477c41374f6784cc88c2c117bd069927216 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 16 Feb 2022 14:10:17 +0100 Subject: [PATCH 04/60] Add logpdf to normal distribution --- include/boost/math/distributions/normal.hpp | 36 +++++++++++++++++++++ test/test_normal.cpp | 19 +++++++++++ 2 files changed, 55 insertions(+) diff --git a/include/boost/math/distributions/normal.hpp b/include/boost/math/distributions/normal.hpp index 73ada53b6f..063800ca92 100644 --- a/include/boost/math/distributions/normal.hpp +++ b/include/boost/math/distributions/normal.hpp @@ -165,6 +165,42 @@ inline RealType pdf(const normal_distribution& dist, const Rea return result; } // pdf +template +inline RealType logpdf(const normal_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + + RealType sd = dist.standard_deviation(); + RealType mean = dist.mean(); + + static const char* function = "boost::math::logpdf(const normal_distribution<%1%>&, %1%)"; + + RealType result = 0; + if(false == detail::check_scale(function, sd, &result, Policy())) + { + return result; + } + if(false == detail::check_location(function, mean, &result, Policy())) + { + return result; + } + if((boost::math::isinf)(x)) + { + return 0; // pdf + and - infinity is zero. + } + if(false == detail::check_x(function, x, &result, Policy())) + { + return result; + } + + const RealType pi = boost::math::constants::pi(); + const RealType half = boost::math::constants::half(); + + result = -log(sd) - half*log(2*pi) - (x-mean)*(x-mean)/(2*sd*sd); + + return result; +} + template inline RealType cdf(const normal_distribution& dist, const RealType& x) { diff --git a/test/test_normal.cpp b/test/test_normal.cpp index b8d0024872..45647fc46a 100644 --- a/test/test_normal.cpp +++ b/test/test_normal.cpp @@ -126,6 +126,7 @@ void test_spots(RealType) { // No longer allow x to be NaN, then these tests should throw. BOOST_MATH_CHECK_THROW(pdf(N01, +std::numeric_limits::quiet_NaN()), std::domain_error); // x = NaN + BOOST_MATH_CHECK_THROW(logpdf(N01, +std::numeric_limits::quiet_NaN()), std::domain_error); // x = NaN BOOST_MATH_CHECK_THROW(cdf(N01, +std::numeric_limits::quiet_NaN()), std::domain_error); // x = NaN BOOST_MATH_CHECK_THROW(cdf(complement(N01, +std::numeric_limits::quiet_NaN())), std::domain_error); // x = + infinity BOOST_MATH_CHECK_THROW(quantile(N01, +std::numeric_limits::quiet_NaN()), std::domain_error); // p = + infinity @@ -215,6 +216,22 @@ void test_spots(RealType) static_cast(0.3989422804014326779399460599343818684759L / 5), tolerance); + // + // Tests for logpdf + // + BOOST_CHECK_CLOSE( + logpdf(normal_distribution(), static_cast(0)), + log(static_cast(0.3989422804014326779399460599343818684759L)), // 1/sqrt(2*pi) + tolerance); + BOOST_CHECK_CLOSE( + logpdf(normal_distribution(3), static_cast(3)), + log(static_cast(0.3989422804014326779399460599343818684759L)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(normal_distribution(3, 5), static_cast(3)), + log(static_cast(0.3989422804014326779399460599343818684759L / 5)), + tolerance); + // // Spot checks for mean = -5, sd = 6: // @@ -307,6 +324,8 @@ void test_spots(RealType) BOOST_MATH_CHECK_THROW(pdf(normal_distribution(0, 0), 0), std::domain_error); BOOST_MATH_CHECK_THROW(pdf(normal_distribution(0, -1), 0), std::domain_error); + BOOST_MATH_CHECK_THROW(logpdf(normal_distribution(0, 0), 0), std::domain_error); + BOOST_MATH_CHECK_THROW(logpdf(normal_distribution(0, -1), 0), std::domain_error); BOOST_MATH_CHECK_THROW(quantile(normal_distribution(0, 1), -1), std::domain_error); BOOST_MATH_CHECK_THROW(quantile(normal_distribution(0, 1), 2), std::domain_error); } // template void test_spots(RealType) From eb55d0abae13329b268f3124158f297453add683 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 16 Feb 2022 15:04:21 +0100 Subject: [PATCH 05/60] Add logpdf to poisson distribution --- include/boost/math/distributions/poisson.hpp | 37 ++++++++++++++++++++ test/test_poisson.cpp | 25 +++++++++++++ 2 files changed, 62 insertions(+) diff --git a/include/boost/math/distributions/poisson.hpp b/include/boost/math/distributions/poisson.hpp index 533c31a80d..921b9f027c 100644 --- a/include/boost/math/distributions/poisson.hpp +++ b/include/boost/math/distributions/poisson.hpp @@ -47,6 +47,7 @@ #include #include +#include namespace boost { @@ -281,6 +282,42 @@ namespace boost return boost::math::gamma_p_derivative(k+1, mean, Policy()); } // pdf + template + RealType logpdf(const poisson_distribution& dist, const RealType& k) + { + BOOST_FPU_EXCEPTION_GUARD + + BOOST_MATH_STD_USING // for ADL of std functions. + using boost::math::tgamma; + + RealType mean = dist.mean(); + // Error check: + RealType result = 0; + if(false == poisson_detail::check_dist_and_k( + "boost::math::pdf(const poisson_distribution<%1%>&, %1%)", + mean, + k, + &result, Policy())) + { + return result; + } + + // Special case of mean zero, regardless of the number of events k. + if (mean == 0) + { // Probability for any k is zero. + return std::numeric_limits::quiet_NaN(); + } + + // Special case where k and lambda are both positive + if(k > 0 && mean > 0) + { + return -log(tgamma(k+1)) + k*log(mean) - mean; + } + + result = log(pdf(dist, k)); + return result; + } + template RealType cdf(const poisson_distribution& dist, const RealType& k) { // Cumulative Distribution Function Poisson. diff --git a/test/test_poisson.cpp b/test/test_poisson.cpp index 934395fe8e..9b75ce162f 100644 --- a/test/test_poisson.cpp +++ b/test/test_poisson.cpp @@ -208,6 +208,31 @@ void test_spots(RealType) static_cast(20)), // K>> mean static_cast(8.277463646553730E-009), // probability. tolerance); + + // LOGPDF + BOOST_CHECK_CLOSE( + logpdf(poisson_distribution(static_cast(4)), // mean 4. + static_cast(0)), + log(static_cast(1.831563888873410E-002)), // probability. + tolerance); + + BOOST_CHECK_CLOSE( + logpdf(poisson_distribution(static_cast(4)), // mean 4. + static_cast(2)), + log(static_cast(1.465251111098740E-001)), // probability. + tolerance); + + BOOST_CHECK_CLOSE( + logpdf(poisson_distribution(static_cast(20)), // mean big. + static_cast(1)), // k small + log(static_cast(4.122307244877130E-008)), // probability. + tolerance); + + BOOST_CHECK_CLOSE( + logpdf(poisson_distribution(static_cast(4)), // mean 4. + static_cast(20)), // K>> mean + log(static_cast(8.277463646553730E-009)), // probability. + tolerance); // CDF BOOST_CHECK_CLOSE( From 598623372675871b7d341494fa8579abc87e798f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 16 Feb 2022 17:29:23 +0100 Subject: [PATCH 06/60] Add logpdf to exponential distribution --- .../boost/math/distributions/exponential.hpp | 18 +++++++++++++ test/test_exponential_dist.cpp | 25 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/include/boost/math/distributions/exponential.hpp b/include/boost/math/distributions/exponential.hpp index ba7eae927f..03295e94e9 100644 --- a/include/boost/math/distributions/exponential.hpp +++ b/include/boost/math/distributions/exponential.hpp @@ -127,6 +127,24 @@ inline RealType pdf(const exponential_distribution& dist, cons return result; } // pdf +template +inline RealType logpdf(const exponential_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + + static const char* function = "boost::math::logpdf(const exponential_distribution<%1%>&, %1%)"; + + RealType lambda = dist.lambda(); + RealType result = 0; + if(0 == detail::verify_lambda(function, lambda, &result, Policy())) + return result; + if(0 == detail::verify_exp_x(function, x, &result, Policy())) + return result; + + result = log(lambda) - lambda * x; + return result; +} // logpdf + template inline RealType cdf(const exponential_distribution& dist, const RealType& x) { diff --git a/test/test_exponential_dist.cpp b/test/test_exponential_dist.cpp index c0423bf0de..12d9fcad19 100644 --- a/test/test_exponential_dist.cpp +++ b/test/test_exponential_dist.cpp @@ -189,6 +189,31 @@ void test_spots(RealType T) static_cast(9.0799859524969703071183031121101e-5L), // probability. tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + exponential_distribution(0.5), + static_cast(0.125)), // x + log(static_cast(0.46970653140673789305985541231115L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + exponential_distribution(0.5), + static_cast(5)), // x + log(static_cast(0.04104249931194939758476433723358L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + exponential_distribution(2), + static_cast(0.125)), // x + log(static_cast(1.5576015661428097364903405339566L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + exponential_distribution(2), + static_cast(5)), // x + log(static_cast(9.0799859524969703071183031121101e-5L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( ::boost::math::mean( exponential_distribution(2)), From 68d000cd4268ddbe0df111581953e711c2601c1a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 16 Feb 2022 19:01:42 +0100 Subject: [PATCH 07/60] Add logpdf to gamma distribution --- include/boost/math/distributions/gamma.hpp | 36 ++++++++++++++++++++++ test/test_gamma_dist.cpp | 8 +++++ 2 files changed, 44 insertions(+) diff --git a/include/boost/math/distributions/gamma.hpp b/include/boost/math/distributions/gamma.hpp index 8a3414d312..f7838ed0da 100644 --- a/include/boost/math/distributions/gamma.hpp +++ b/include/boost/math/distributions/gamma.hpp @@ -17,6 +17,7 @@ #include #include +#include namespace boost{ namespace math { @@ -147,6 +148,41 @@ inline RealType pdf(const gamma_distribution& dist, const Real return result; } // pdf +template +inline RealType logpdf(const gamma_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + using boost::math::tgamma; + + static const char* function = "boost::math::logpdf(const gamma_distribution<%1%>&, %1%)"; + + RealType k = dist.shape(); + RealType theta = dist.scale(); + + RealType result = 0; + if(false == detail::check_gamma(function, theta, k, &result, Policy())) + return result; + if(false == detail::check_gamma_x(function, x, &result, Policy())) + return result; + + if(x == 0) + { + return std::numeric_limits::quiet_NaN(); + } + + // The following calculation does not always work with float so take the naive road out + BOOST_IF_CONSTEXPR(std::is_same::value) + { + result = log(pdf(dist, x)); + } + else + { + result = -k*log(theta) + (k-1)*log(x) - log(tgamma(k)) - (x/theta); + } + + return result; +} // logpdf + template inline RealType cdf(const gamma_distribution& dist, const RealType& x) { diff --git a/test/test_gamma_dist.cpp b/test/test_gamma_dist.cpp index 682535c283..b7776c79cb 100644 --- a/test/test_gamma_dist.cpp +++ b/test/test_gamma_dist.cpp @@ -88,6 +88,14 @@ void check_gamma(RealType shape, RealType scale, RealType x, RealType p, RealTyp x), // random variable. NaivePDF(shape, scale, x), // PDF tol); // %tolerance. + + // LOGPDF: + BOOST_CHECK_CLOSE( + boost::math::logpdf( + gamma_distribution(shape, scale), // distribution. + x), // random variable. + log(boost::math::pdf(gamma_distribution(shape, scale), x)), // PDF + tol); // %tolerance. } template From 079ebcabb59fb82f05d132f539b1e4c8a87d834f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 17 Feb 2022 11:32:22 +0100 Subject: [PATCH 08/60] Attempt to increase resolution of logpdf for normal distribution Fails with long doubles --- include/boost/math/distributions/normal.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/boost/math/distributions/normal.hpp b/include/boost/math/distributions/normal.hpp index 063800ca92..117a20e59d 100644 --- a/include/boost/math/distributions/normal.hpp +++ b/include/boost/math/distributions/normal.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace boost{ namespace math{ @@ -165,10 +166,12 @@ inline RealType pdf(const normal_distribution& dist, const Rea return result; } // pdf -template +template ::value, bool>::type = true> inline RealType logpdf(const normal_distribution& dist, const RealType& x) { BOOST_MATH_STD_USING // for ADL of std functions + using std::fma; + using std::pow; RealType sd = dist.standard_deviation(); RealType mean = dist.mean(); @@ -193,10 +196,10 @@ inline RealType logpdf(const normal_distribution& dist, const return result; } - const RealType pi = boost::math::constants::pi(); - const RealType half = boost::math::constants::half(); + constexpr RealType two_pi = boost::math::constants::two_pi(); + constexpr RealType half = boost::math::constants::half(); - result = -log(sd) - half*log(2*pi) - (x-mean)*(x-mean)/(2*sd*sd); + result = fma(-half, log(two_pi), -log(sd)) - pow(x-mean, RealType(2))/(2*pow(sd, RealType(2))); return result; } From 4bd4951a37cf4f0cf21c2bbe633a279708ee9036 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 17 Feb 2022 14:15:55 +0100 Subject: [PATCH 09/60] Remove normal dist logpdf in favor of default --- include/boost/math/distributions/normal.hpp | 38 --------------------- 1 file changed, 38 deletions(-) diff --git a/include/boost/math/distributions/normal.hpp b/include/boost/math/distributions/normal.hpp index 117a20e59d..4b3c490a4e 100644 --- a/include/boost/math/distributions/normal.hpp +++ b/include/boost/math/distributions/normal.hpp @@ -166,44 +166,6 @@ inline RealType pdf(const normal_distribution& dist, const Rea return result; } // pdf -template ::value, bool>::type = true> -inline RealType logpdf(const normal_distribution& dist, const RealType& x) -{ - BOOST_MATH_STD_USING // for ADL of std functions - using std::fma; - using std::pow; - - RealType sd = dist.standard_deviation(); - RealType mean = dist.mean(); - - static const char* function = "boost::math::logpdf(const normal_distribution<%1%>&, %1%)"; - - RealType result = 0; - if(false == detail::check_scale(function, sd, &result, Policy())) - { - return result; - } - if(false == detail::check_location(function, mean, &result, Policy())) - { - return result; - } - if((boost::math::isinf)(x)) - { - return 0; // pdf + and - infinity is zero. - } - if(false == detail::check_x(function, x, &result, Policy())) - { - return result; - } - - constexpr RealType two_pi = boost::math::constants::two_pi(); - constexpr RealType half = boost::math::constants::half(); - - result = fma(-half, log(two_pi), -log(sd)) - pow(x-mean, RealType(2))/(2*pow(sd, RealType(2))); - - return result; -} - template inline RealType cdf(const normal_distribution& dist, const RealType& x) { From 6f1cda5bb38d9a87582248bc271cf70662737747 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 11:16:33 +0100 Subject: [PATCH 10/60] Loosen tolerance on logpdf for long doubles for normal dist --- include/boost/math/distributions/normal.hpp | 36 +++++++++++++++++++++ test/test_normal.cpp | 11 +++++++ 2 files changed, 47 insertions(+) diff --git a/include/boost/math/distributions/normal.hpp b/include/boost/math/distributions/normal.hpp index 4b3c490a4e..a126e85fac 100644 --- a/include/boost/math/distributions/normal.hpp +++ b/include/boost/math/distributions/normal.hpp @@ -166,6 +166,42 @@ inline RealType pdf(const normal_distribution& dist, const Rea return result; } // pdf +template +inline RealType logpdf(const normal_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + + RealType sd = dist.standard_deviation(); + RealType mean = dist.mean(); + + static const char* function = "boost::math::logpdf(const normal_distribution<%1%>&, %1%)"; + + RealType result = 0; + if(false == detail::check_scale(function, sd, &result, Policy())) + { + return result; + } + if(false == detail::check_location(function, mean, &result, Policy())) + { + return result; + } + if((boost::math::isinf)(x)) + { + return 0; // pdf + and - infinity is zero. + } + if(false == detail::check_x(function, x, &result, Policy())) + { + return result; + } + + const RealType pi = boost::math::constants::pi(); + const RealType half = boost::math::constants::half(); + + result = -log(sd) - half*log(2*pi) - (x-mean)*(x-mean)/(2*sd*sd); + + return result; +} + template inline RealType cdf(const normal_distribution& dist, const RealType& x) { diff --git a/test/test_normal.cpp b/test/test_normal.cpp index 45647fc46a..ef984d5e63 100644 --- a/test/test_normal.cpp +++ b/test/test_normal.cpp @@ -41,6 +41,8 @@ using std::setprecision; #include using std::numeric_limits; +#include + using std::log; template RealType NaivePDF(RealType mean, RealType sd, RealType x) @@ -219,6 +221,13 @@ void test_spots(RealType) // // Tests for logpdf // + RealType temp_tol = tolerance; + + BOOST_IF_CONSTEXPR (std::is_same::value) + { + tolerance *= 100; + } + BOOST_CHECK_CLOSE( logpdf(normal_distribution(), static_cast(0)), log(static_cast(0.3989422804014326779399460599343818684759L)), // 1/sqrt(2*pi) @@ -232,6 +241,8 @@ void test_spots(RealType) log(static_cast(0.3989422804014326779399460599343818684759L / 5)), tolerance); + tolerance = temp_tol; + // // Spot checks for mean = -5, sd = 6: // From 93593fb8f2856a4e5eb9d69a894d131f138451a8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 12:19:15 +0100 Subject: [PATCH 11/60] Add logpdf to chi squared distribution [ci skip] --- .../boost/math/distributions/chi_squared.hpp | 41 +++++++++++++++++++ test/test_chi_squared.cpp | 4 ++ 2 files changed, 45 insertions(+) diff --git a/include/boost/math/distributions/chi_squared.hpp b/include/boost/math/distributions/chi_squared.hpp index e97bee7ce7..a81197a851 100644 --- a/include/boost/math/distributions/chi_squared.hpp +++ b/include/boost/math/distributions/chi_squared.hpp @@ -131,6 +131,47 @@ RealType pdf(const chi_squared_distribution& dist, const RealT return gamma_p_derivative(degrees_of_freedom / 2, chi_square / 2, Policy()) / 2; } // pdf +template +RealType logpdf(const chi_squared_distribution& dist, const RealType& chi_square) +{ + BOOST_MATH_STD_USING // for ADL of std functions + RealType k = dist.degrees_of_freedom(); + // Error check: + RealType error_result; + + static const char* function = "boost::math::logpdf(const chi_squared_distribution<%1%>&, %1%)"; + + if(false == detail::check_df(function, k, &error_result, Policy())) + { + return error_result; + } + + if((chi_square < 0) || !(boost::math::isfinite)(chi_square)) + { + return policies::raise_domain_error( + function, "Chi Square parameter was %1%, but must be > 0 !", chi_square, Policy()); + } + + if(chi_square == 0) + { + // Handle special cases: + if(k < 2) + { + return policies::raise_overflow_error(function, 0, Policy()); + } + else if(k == 2) + { + return -boost::math::constants::ln_two(); + } + else + { + return -std::numeric_limits::infinity(); + } + } + + return log(pdf(dist, chi_square)); +} // logpdf + template inline RealType cdf(const chi_squared_distribution& dist, const RealType& chi_square) { diff --git a/test/test_chi_squared.cpp b/test/test_chi_squared.cpp index 41e4dd5e50..cc7747a6c0 100644 --- a/test/test_chi_squared.cpp +++ b/test/test_chi_squared.cpp @@ -36,6 +36,8 @@ using std::cout; using std::endl; #include using std::numeric_limits; +#include +using std::log; template RealType naive_pdf(RealType df, RealType x) @@ -59,6 +61,8 @@ void test_spot( cdf(dist, cs), P, tol); BOOST_CHECK_CLOSE( pdf(dist, cs), naive_pdf(dist.degrees_of_freedom(), cs), tol); + BOOST_CHECK_CLOSE( + logpdf(dist, cs), log(pdf(dist, cs)), tol); if((P < 0.99) && (Q < 0.99)) { // From 208a1bef46f3ea16293a81feb002d0632633cf75 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 12:35:28 +0100 Subject: [PATCH 12/60] Add logpdf to extreme value distribution [ci skip] --- .../math/distributions/extreme_value.hpp | 25 +++++++++++++++++++ test/test_extreme_value.cpp | 19 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/boost/math/distributions/extreme_value.hpp b/include/boost/math/distributions/extreme_value.hpp index d503b31bc9..18b05071da 100644 --- a/include/boost/math/distributions/extreme_value.hpp +++ b/include/boost/math/distributions/extreme_value.hpp @@ -122,6 +122,31 @@ inline RealType pdf(const extreme_value_distribution& dist, co return result; } // pdf +template +inline RealType logpdf(const extreme_value_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + + static const char* function = "boost::math::logpdf(const extreme_value_distribution<%1%>&, %1%)"; + + RealType a = dist.location(); + RealType b = dist.scale(); + RealType result = 0; + if(0 == detail::verify_scale_b(function, b, &result, Policy())) + return result; + if(0 == detail::check_finite(function, a, &result, Policy())) + return result; + if((boost::math::isinf)(x)) + return 0.0f; + if(0 == detail::check_x(function, x, &result, Policy())) + return result; + RealType e = (a - x) / b; + if(e < tools::log_max_value()) + result = log(1/b) + e - exp(e); + // else.... result *must* be zero since exp(e) is infinite... + return result; +} // pdf + template inline RealType cdf(const extreme_value_distribution& dist, const RealType& x) { diff --git a/test/test_extreme_value.cpp b/test/test_extreme_value.cpp index f61731647e..2493f466df 100644 --- a/test/test_extreme_value.cpp +++ b/test/test_extreme_value.cpp @@ -120,6 +120,25 @@ void test_spots(RealType) static_cast(0.11522236828583456431277265757312L), // probability. tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + extreme_value_distribution(0.5, 2), + static_cast(0.125)), // x + log(static_cast(0.18052654830890205978204427757846L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + extreme_value_distribution(1, 3), + static_cast(5)), // x + log(static_cast(0.0675057324099851209129017326286L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + extreme_value_distribution(1, 3), + static_cast(0)), // x + log(static_cast(0.11522236828583456431277265757312L)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( ::boost::math::mean( extreme_value_distribution(2, 3)), From 570b40e14aae9091da5aebd454ccce41c9bc210b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 13:08:50 +0100 Subject: [PATCH 13/60] Add logpdf to inverse gamma distribution [ci skip] --- .../math/distributions/inverse_gamma.hpp | 38 +++++++++++++++++++ test/test_inverse_gamma_distribution.cpp | 3 ++ 2 files changed, 41 insertions(+) diff --git a/include/boost/math/distributions/inverse_gamma.hpp b/include/boost/math/distributions/inverse_gamma.hpp index 9266bc22f6..139e8982a6 100644 --- a/include/boost/math/distributions/inverse_gamma.hpp +++ b/include/boost/math/distributions/inverse_gamma.hpp @@ -28,6 +28,7 @@ #include #include +#include namespace boost{ namespace math { @@ -195,6 +196,43 @@ inline RealType pdf(const inverse_gamma_distribution& dist, co return result; } // pdf +template +inline RealType logpdf(const inverse_gamma_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + using boost::math::tgamma; + + static const char* function = "boost::math::logpdf(const inverse_gamma_distribution<%1%>&, %1%)"; + + RealType shape = dist.shape(); + RealType scale = dist.scale(); + + RealType result = 0; + if(false == detail::check_inverse_gamma(function, scale, shape, &result, Policy())) + { // distribution parameters bad. + return result; + } + if(x == 0) + { // Treat random variate zero as a special case. + return 0; + } + else if(false == detail::check_inverse_gamma_x(function, x, &result, Policy())) + { // x bad. + return result; + } + result = scale / x; + if(result < tools::min_value()) + return 0; // random variable is infinite or so close as to make no difference. + + // x * x may under or overflow, likewise our result + if (!boost::math::isfinite(x*x)) + { + return policies::raise_overflow_error(function, "PDF is infinite.", Policy()); + } + + return shape * log(scale) + (-shape-1)*log(x) - log(tgamma(shape)) - (scale/x); +} // pdf + template inline RealType cdf(const inverse_gamma_distribution& dist, const RealType& x) { diff --git a/test/test_inverse_gamma_distribution.cpp b/test/test_inverse_gamma_distribution.cpp index bad1bb158b..68b238fbc8 100644 --- a/test/test_inverse_gamma_distribution.cpp +++ b/test/test_inverse_gamma_distribution.cpp @@ -72,6 +72,9 @@ void test_spot( BOOST_CHECK_CLOSE_FRACTION( // Compare to naive formula (might be less accurate). pdf(dist, x), naive_pdf(dist.shape(), dist.scale(), x), tol); + BOOST_CHECK_CLOSE_FRACTION( // Compare direct logpdf to naive log(pdf()) + logpdf(dist, x), log(pdf(dist,x)), tol); + BOOST_CHECK_CLOSE_FRACTION( // Compare to expected CDF. cdf(dist, x), P, tol); From 269bf5947f678dbb64b756ce3bc6259898900b55 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 13:39:22 +0100 Subject: [PATCH 14/60] Add logpdf to inverse gaussian distribution [ci skip] --- .../math/distributions/inverse_gamma.hpp | 2 +- .../math/distributions/inverse_gaussian.hpp | 37 +++++++++++++++++++ test/test_inverse_gaussian.cpp | 35 +++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/include/boost/math/distributions/inverse_gamma.hpp b/include/boost/math/distributions/inverse_gamma.hpp index 139e8982a6..91da8f34dd 100644 --- a/include/boost/math/distributions/inverse_gamma.hpp +++ b/include/boost/math/distributions/inverse_gamma.hpp @@ -225,7 +225,7 @@ inline RealType logpdf(const inverse_gamma_distribution& dist, return 0; // random variable is infinite or so close as to make no difference. // x * x may under or overflow, likewise our result - if (!boost::math::isfinite(x*x)) + if (!(boost::math::isfinite)(x*x)) { return policies::raise_overflow_error(function, "PDF is infinite.", Policy()); } diff --git a/include/boost/math/distributions/inverse_gaussian.hpp b/include/boost/math/distributions/inverse_gaussian.hpp index 757630a40d..849364da4a 100644 --- a/include/boost/math/distributions/inverse_gaussian.hpp +++ b/include/boost/math/distributions/inverse_gaussian.hpp @@ -174,6 +174,43 @@ inline RealType pdf(const inverse_gaussian_distribution& dist, return result; } // pdf +template +inline RealType logpdf(const inverse_gaussian_distribution& dist, const RealType& x) +{ // Probability Density Function + BOOST_MATH_STD_USING // for ADL of std functions + + RealType scale = dist.scale(); + RealType mean = dist.mean(); + RealType result = 0; + static const char* function = "boost::math::logpdf(const inverse_gaussian_distribution<%1%>&, %1%)"; + if(false == detail::check_scale(function, scale, &result, Policy())) + { + return result; + } + if(false == detail::check_location(function, mean, &result, Policy())) + { + return result; + } + if(false == detail::check_x_gt0(function, mean, &result, Policy())) + { + return result; + } + if(false == detail::check_positive_x(function, x, &result, Policy())) + { + return result; + } + + if (x == 0) + { + return 0; // Convenient, even if not defined mathematically. + } + + const RealType two_pi = boost::math::constants::two_pi(); + + result = (-scale*pow(mean - x, RealType(2))/(mean*mean*x) + log(scale) - 3*log(x) - log(two_pi)) / 2; + return result; +} // pdf + template inline RealType cdf(const inverse_gaussian_distribution& dist, const RealType& x) { // Cumulative Density Function. diff --git a/test/test_inverse_gaussian.cpp b/test/test_inverse_gaussian.cpp index 2bb5303122..68012d48a2 100644 --- a/test/test_inverse_gaussian.cpp +++ b/test/test_inverse_gaussian.cpp @@ -36,6 +36,8 @@ using std::endl; using std::setprecision; #include using std::numeric_limits; +#include +using std::log; template void check_inverse_gaussian(RealType mean, RealType scale, RealType x, RealType p, RealType q, RealType tol) @@ -207,21 +209,29 @@ BOOST_AUTO_TEST_CASE( test_main ) // formatC(SuppDists::dinverse_gaussian(1, 1, 1), digits=17) ... BOOST_CHECK_CLOSE_FRACTION( // x = 1 pdf(w11, 1.), static_cast(0.3989422804014327), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( // x = 1 + logpdf(w11, 1.), static_cast(log(0.3989422804014327)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 1.), static_cast(0.66810200122317065), 10 * tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( pdf(w11, 0.1), static_cast(0.21979480031862672), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w11, 0.1), static_cast(log(0.21979480031862672)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 0.1), static_cast(0.0040761113207110162), 10 * tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( // small x pdf(w11, 0.01), static_cast(2.0811768202028392e-19), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( // small x + logpdf(w11, 0.01), static_cast(log(2.0811768202028392e-19)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 0.01), static_cast(4.122313403318778e-23), 10 * tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( // smaller x pdf(w11, 0.001), static_cast(2.4420044378793562e-213), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( // smaller x + logpdf(w11, 0.001), static_cast(log(2.4420044378793562e-213)), tolfeweps); // pdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 0.001), static_cast(4.8791443010851493e-219), 1000 * tolfeweps); // cdf // 4.8791443010859224e-219 versus 4.8791443010851493e-219 so still 14 decimal digits. @@ -240,25 +250,35 @@ BOOST_AUTO_TEST_CASE( test_main ) BOOST_CHECK_CLOSE_FRACTION( pdf(w11, 0.5), static_cast(0.87878257893544476), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w11, 0.5), static_cast(log(0.87878257893544476)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 0.5), static_cast(0.3649755481729598), tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( pdf(w11, 2), static_cast(0.10984782236693059), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w11, 2), static_cast(log(0.10984782236693059)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 2), static_cast(.88547542598600637), tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( pdf(w11, 10), static_cast(0.00021979480031862676), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w11, 10), static_cast(log(0.00021979480031862676)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 10), static_cast(0.99964958546279115), tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( pdf(w11, 100), static_cast(2.0811768202028246e-25), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w11, 100), static_cast(log(2.0811768202028246e-25)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 100), static_cast(1), tolfeweps); // cdf BOOST_CHECK_CLOSE_FRACTION( pdf(w11, 1000), static_cast(2.4420044378793564e-222), 10 * tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w11, 1000), static_cast(log(2.4420044378793564e-222)), 10 * tolfeweps); // pdf BOOST_CHECK_CLOSE_FRACTION( cdf(w11, 1000), static_cast(1.), tolfeweps); // cdf @@ -295,26 +315,37 @@ BOOST_AUTO_TEST_CASE( test_main ) // =================== BOOST_CHECK_CLOSE_FRACTION( // formatC(SuppDists::dinvGauss(1, 2, 3), digits=17) "0.47490884963330904" pdf(w23, 1.), static_cast(0.47490884963330904), tolfeweps ); // pdf - + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w23, 1.), static_cast(log(0.47490884963330904)), tolfeweps ); // logpdf BOOST_CHECK_CLOSE_FRACTION( pdf(w23, 0.1), static_cast(2.8854207087665401e-05), tolfeweps * 2); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w23, 0.1), static_cast(log(2.8854207087665401e-05)), tolfeweps * 2); // logpdf //2.8854207087665452e-005 2.8854207087665401e-005 BOOST_CHECK_CLOSE_FRACTION( pdf(w23, 10.), static_cast(0.0019822751498574636), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w23, 10.), static_cast(log(0.0019822751498574636)), tolfeweps); // logpdf BOOST_CHECK_CLOSE_FRACTION( pdf(w23, 10.), static_cast(0.0019822751498574636), tolfeweps); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w23, 10.), static_cast(log(0.0019822751498574636)), tolfeweps); // logpdf // Bigger changes in mean and scale. inverse_gaussian w012(0.1, 2); BOOST_CHECK_CLOSE_FRACTION( pdf(w012, 1.), static_cast(3.7460367141230404e-36), tolfeweps ); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w012, 1.), static_cast(log(3.7460367141230404e-36)), tolfeweps ); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w012, 1.), static_cast(1), tolfeweps ); // pdf inverse_gaussian w0110(0.1, 10); BOOST_CHECK_CLOSE_FRACTION( pdf(w0110, 1.), static_cast(1.6279643678071011e-176), 100 * tolfeweps ); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w0110, 1.), static_cast(log(1.6279643678071011e-176)), 100 * tolfeweps ); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w0110, 1.), static_cast(1), tolfeweps ); // cdf BOOST_CHECK_CLOSE_FRACTION( @@ -323,6 +354,8 @@ BOOST_AUTO_TEST_CASE( test_main ) BOOST_CHECK_CLOSE_FRACTION( pdf(w0110, 0.1), static_cast(39.894228040143268), tolfeweps ); // pdf + BOOST_CHECK_CLOSE_FRACTION( + logpdf(w0110, 0.1), static_cast(log(39.894228040143268)), tolfeweps ); // logpdf BOOST_CHECK_CLOSE_FRACTION( cdf(w0110, 0.1), static_cast(0.51989761564832704), 10 * tolfeweps ); // cdf From cd4f2b7cbe080ff018d726182ae9ae0c1529f99b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 14:11:39 +0100 Subject: [PATCH 15/60] Add logpdf to laplace distribution [ci skip] --- include/boost/math/distributions/laplace.hpp | 38 ++++++++++++++++++++ test/test_laplace.cpp | 36 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/boost/math/distributions/laplace.hpp b/include/boost/math/distributions/laplace.hpp index 9b268f3f33..081512240e 100644 --- a/include/boost/math/distributions/laplace.hpp +++ b/include/boost/math/distributions/laplace.hpp @@ -150,6 +150,44 @@ inline RealType pdf(const laplace_distribution& dist, const Re return result; } // pdf +template +inline RealType logpdf(const laplace_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + + // Checking function argument + RealType result = 0; + const char* function = "boost::math::logpdf(const laplace_distribution<%1%>&, %1%))"; + + // Check scale and location. + if (false == dist.check_parameters(function, &result)) + return result; + // Special pdf values. + if((boost::math::isinf)(x)) + { + return 0; // pdf + and - infinity is zero. + } + if (false == detail::check_x(function, x, &result, Policy())) + return result; + + const RealType mu = dist.scale(); + const RealType b = dist.location(); + + // if b is 0 avoid divde by 0 error + if(abs(b) < std::numeric_limits::epsilon()) + { + result = log(pdf(dist, x)); + } + else + { + // General case + const RealType log2 = boost::math::constants::ln_two(); + result = -abs(x-mu)/b - log(b) - log2; + } + + return result; +} // logpdf + template inline RealType cdf(const laplace_distribution& dist, const RealType& x) { diff --git a/test/test_laplace.cpp b/test/test_laplace.cpp index 9167547f91..716c738736 100644 --- a/test/test_laplace.cpp +++ b/test/test_laplace.cpp @@ -67,6 +67,8 @@ Test 8: test_extreme_function_arguments() #include #include "test_out_of_range.hpp" using boost::math::laplace_distribution; +#include +using std::log; /* #include @@ -211,6 +213,12 @@ void test_pdf_cdf_ocatave() static_cast(0.067667641618306345946999747486242201703815773119812L), tolerance); + BOOST_CHECK_CLOSE( + logpdf(laplace_distribution(), static_cast(-2.L)), + // static_cast(0.06766764161831L), + log(static_cast(0.067667641618306345946999747486242201703815773119812L)), + tolerance); + BOOST_CHECK_CLOSE( cdf(laplace_distribution(), static_cast(-2.L)), //static_cast(0.06766764161831L), @@ -223,6 +231,12 @@ void test_pdf_cdf_ocatave() static_cast(0.18393972058572116079776188508073043372290556554506L), tolerance); + BOOST_CHECK_CLOSE( + logpdf(laplace_distribution(), static_cast(-1.L)), + //static_cast(0.18393972058572L), + log(static_cast(0.18393972058572116079776188508073043372290556554506L)), + tolerance); + BOOST_CHECK_CLOSE( cdf(laplace_distribution(), static_cast(-1.L)), static_cast(0.18393972058572L), @@ -245,6 +259,11 @@ void test_pdf_cdf_ocatave() static_cast(0.5L), tolerance); + BOOST_CHECK_CLOSE( + logpdf(laplace_distribution(), static_cast(0.0L)), + log(static_cast(0.5L)), + tolerance); + BOOST_CHECK_CLOSE( cdf(laplace_distribution(), static_cast(0.0L)), static_cast(0.5L), @@ -256,6 +275,11 @@ void test_pdf_cdf_ocatave() static_cast(0.30326532985631671180189976749559022672095906778368L), tolerance); + BOOST_CHECK_CLOSE( + logpdf(laplace_distribution(), static_cast(0.5L)), + log(static_cast(0.30326532985631671180189976749559022672095906778368L)), + tolerance); + BOOST_CHECK_CLOSE( cdf(laplace_distribution(), static_cast(0.5L)), // static_cast(0.69673467014368L), @@ -268,6 +292,12 @@ void test_pdf_cdf_ocatave() static_cast(0.18393972058572116079776188508073043372290556554506L), tolerance); + BOOST_CHECK_CLOSE( + logpdf(laplace_distribution(), static_cast(1.0L)), + // static_cast(0.18393972058572L), + log(static_cast(0.18393972058572116079776188508073043372290556554506L)), + tolerance); + BOOST_CHECK_CLOSE( cdf(laplace_distribution(), static_cast(1.00000000000000L)), // static_cast(0.81606027941428L), @@ -280,6 +310,12 @@ void test_pdf_cdf_ocatave() static_cast(0.067667641618306345946999747486242201703815772944649L), tolerance); + BOOST_CHECK_CLOSE( + logpdf(laplace_distribution(), static_cast(2.0L)), + // static_cast(0.06766764161831L), + log(static_cast(0.067667641618306345946999747486242201703815772944649L)), + tolerance); + BOOST_CHECK_CLOSE( cdf(laplace_distribution(), static_cast(2.0L)), // static_cast(0.93233235838169L), From 7681a4eded4824f8bf7496956f2597d4b4d8ac75 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 15:30:59 +0100 Subject: [PATCH 16/60] Add logpdf to rayleigh distribution [ci skip] --- include/boost/math/distributions/rayleigh.hpp | 26 ++++++++++++ test/test_rayleigh.cpp | 42 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/include/boost/math/distributions/rayleigh.hpp b/include/boost/math/distributions/rayleigh.hpp index cbe934471f..83f5bc8709 100644 --- a/include/boost/math/distributions/rayleigh.hpp +++ b/include/boost/math/distributions/rayleigh.hpp @@ -122,6 +122,32 @@ inline RealType pdf(const rayleigh_distribution& dist, const R return result; } // pdf +template +inline RealType logpdf(const rayleigh_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std function exp. + + const RealType sigma = dist.sigma(); + RealType result = 0; + static const char* function = "boost::math::logpdf(const rayleigh_distribution<%1%>&, %1%)"; + + if(false == detail::verify_sigma(function, sigma, &result, Policy())) + { + return result; + } + if(false == detail::verify_rayleigh_x(function, x, &result, Policy())) + { + return result; + } + if((boost::math::isinf)(x)) + { + return 0; + } + + result = -(x*x)/(2*sigma*sigma) - 2*log(sigma) + log(x); + return result; +} // logpdf + template inline RealType cdf(const rayleigh_distribution& dist, const RealType& x) { diff --git a/test/test_rayleigh.cpp b/test/test_rayleigh.cpp index 388adc68fc..6aa2d2fb83 100644 --- a/test/test_rayleigh.cpp +++ b/test/test_rayleigh.cpp @@ -26,6 +26,8 @@ using std::cout; using std::endl; using std::setprecision; +#include + using std::log; template void test_spot(RealType s, RealType x, RealType p, RealType q, RealType tolerance) @@ -159,6 +161,25 @@ void test_spots(RealType T) static_cast(exp_minus_half() /2), // probability. tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + rayleigh_distribution(1.L), + static_cast(1.L)), // x + log(static_cast(exp_minus_half())), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + rayleigh_distribution(0.5L), + static_cast(0.5L)), // x + log(static_cast(2 * exp_minus_half())), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( + ::boost::math::logpdf( + rayleigh_distribution(2.L), + static_cast(2.L)), // x + log(static_cast(exp_minus_half() /2)), // probability. + tolerance); // % + BOOST_CHECK_CLOSE( ::boost::math::mean( rayleigh_distribution(1.L)), @@ -255,6 +276,27 @@ BOOST_AUTO_TEST_CASE( test_main ) static_cast(exp_minus_half() /2 ), // p 1e-15); // % + BOOST_CHECK_CLOSE_FRACTION( + ::boost::math::logpdf( + rayleigh_distribution(1.), + static_cast(1)), // x + log(static_cast(exp_minus_half())), // p + 1e-15); // % + + BOOST_CHECK_CLOSE_FRACTION( + ::boost::math::logpdf( + rayleigh_distribution(0.5), + static_cast(0.5)), // x + log(static_cast(2 * exp_minus_half())), // p + 1e-15); // % + + BOOST_CHECK_CLOSE_FRACTION( + ::boost::math::logpdf( + rayleigh_distribution(2.), + static_cast(2)), // x + log(static_cast(exp_minus_half() /2 )), // p + 1e-15); // % + BOOST_CHECK_CLOSE_FRACTION( ::boost::math::cdf( rayleigh_distribution(1.), From 2a3cb313eca110524cba2dbc4f4ec941cea95609 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 24 Feb 2022 15:59:11 +0100 Subject: [PATCH 17/60] Add logpdf to weibull distribution --- include/boost/math/distributions/weibull.hpp | 34 +++++++++++++++ test/test_weibull.cpp | 46 ++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/include/boost/math/distributions/weibull.hpp b/include/boost/math/distributions/weibull.hpp index 76246e4ff2..572c96716c 100644 --- a/include/boost/math/distributions/weibull.hpp +++ b/include/boost/math/distributions/weibull.hpp @@ -157,6 +157,40 @@ inline RealType pdf(const weibull_distribution& dist, const Re return result; } +template +inline RealType logpdf(const weibull_distribution& dist, const RealType& x) +{ + BOOST_MATH_STD_USING // for ADL of std functions + + static const char* function = "boost::math::logpdf(const weibull_distribution<%1%>, %1%)"; + + RealType shape = dist.shape(); + RealType scale = dist.scale(); + + RealType result = 0; + if(false == detail::check_weibull(function, scale, shape, &result, Policy())) + return result; + if(false == detail::check_weibull_x(function, x, &result, Policy())) + return result; + + if(x == 0) + { + if(shape == 1) + { + return log(1 / scale); + } + if(shape > 1) + { + return 0; + } + return policies::raise_overflow_error(function, 0, Policy()); + } + + result = log(shape * pow(scale, -shape) * pow(x, shape - 1)) - pow(x / scale, shape); + + return result; +} + template inline RealType cdf(const weibull_distribution& dist, const RealType& x) { diff --git a/test/test_weibull.cpp b/test/test_weibull.cpp index 17e2bffb04..6cacf5402a 100644 --- a/test/test_weibull.cpp +++ b/test/test_weibull.cpp @@ -29,6 +29,8 @@ using std::setprecision; #include using std::numeric_limits; +#include + using std::log; template void check_weibull(RealType shape, RealType scale, RealType x, RealType p, RealType q, RealType tol) @@ -244,6 +246,50 @@ void test_spots(RealType) static_cast(0.551819), tolerance); + // + // Tests for logpdf + // + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(0.25, 0.5), static_cast(0.1)), + log(static_cast(0.856579)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(0.25, 0.5), static_cast(0.5)), + log(static_cast(0.183940)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(0.25, 0.5), static_cast(5)), + log(static_cast(0.015020)), + tolerance * 10); // fewer digits in test value + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(0.5, 2), static_cast(0.1)), + log(static_cast(0.894013)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(0.5, 2), static_cast(0.5)), + log(static_cast(0.303265)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(0.5, 2), static_cast(1)), + log(static_cast(0.174326)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(2, 0.25), static_cast(0.1)), + log(static_cast(2.726860)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(2, 0.25), static_cast(0.5)), + log(static_cast(0.293050)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(3, 2), static_cast(1)), + log(static_cast(0.330936)), + tolerance); + BOOST_CHECK_CLOSE( + logpdf(weibull_distribution(3, 2), static_cast(2)), + log(static_cast(0.551819)), + tolerance); + // // These test values were obtained using the formulas at // http://en.wikipedia.org/wiki/Weibull_distribution From 2519641098102bb9a262cb1a0c2546ff0f7fd69d Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 10 Mar 2022 18:17:43 +0000 Subject: [PATCH 18/60] Two minor 1F1 bugfixes: Prevent recurrence coefficients falling to zero. Allow checked series to progress if the sum temporarily drops to near zero. Add test cases. --- .../detail/hypergeometric_1F1_recurrence.hpp | 8 ++++++++ .../hypergeometric_pFq_checked_series.hpp | 17 ++++++++++++++--- test/test_1F1.hpp | 7 ++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp b/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp index 6f1461ffc9..319ce76795 100644 --- a/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp +++ b/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp @@ -291,6 +291,14 @@ ak += 2; integer_part -= 2; } + if (ak - 1 == b) + { + // When ak - 1 == b are recursion coefficients dissappear to zero and + // we end up with a NaN result. Reduce the recursion steps by 1 to + // avoid this. We rely on |b| small and therefore no infinite recursion. + --ak; + integer_part += 1; + } if (-integer_part > static_cast(policies::get_max_series_iterations())) return policies::raise_evaluation_error(function, "1F1 arguments sit in a range with a so negative that we have no evaluation method, got a = %1%", std::numeric_limits::quiet_NaN(), pol); diff --git a/include/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp b/include/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp index 3745f7e24d..8ed8c8086f 100644 --- a/include/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp +++ b/include/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp @@ -132,6 +132,7 @@ Real scaling_factor = exp(Real(log_scaling_factor)); Real term_m1; long long local_scaling = 0; + bool have_no_correct_bits = false; if ((aj.size() == 1) && (bj.size() == 0)) { @@ -238,10 +239,20 @@ break; if (abs_result * tol > abs(result)) { - // We have no correct bits in the result... just give up! - result = boost::math::policies::raise_evaluation_error("boost::math::hypergeometric_pFq<%1%>", "Cancellation is so severe that no bits in the reuslt are correct, last result was %1%", Real(result * exp(Real(log_scale))), pol); - return std::make_pair(result, result); + // Check if result is so small compared to abs_resuslt that there are no longer any + // correct bits... we require two consecutive passes here before aborting to + // avoid false positives when result transiently drops to near zero then rebounds. + if (have_no_correct_bits) + { + // We have no correct bits in the result... just give up! + result = boost::math::policies::raise_evaluation_error("boost::math::hypergeometric_pFq<%1%>", "Cancellation is so severe that no bits in the reuslt are correct, last result was %1%", Real(result * exp(Real(log_scale))), pol); + return std::make_pair(result, result); + } + else + have_no_correct_bits = true; } + else + have_no_correct_bits = false; term0 = term; } //std::cout << "result = " << result << std::endl; diff --git a/test/test_1F1.hpp b/test/test_1F1.hpp index 2e7805d442..5edf8cdc34 100644 --- a/test/test_1F1.hpp +++ b/test/test_1F1.hpp @@ -162,7 +162,7 @@ void test_spots5(T, const char* type_name) template void test_spots6(T, const char* type_name) { - static const std::array, 91> hypergeometric_1F1_bugs = { { + static const std::array, 93> hypergeometric_1F1_bugs = { { { { static_cast(17955.561660766602), static_cast(9.6968994205831605e-09), static_cast(-82.406154185533524), SC_(6.98056008378736714088730927132364938220428678e-11) }}, { { static_cast(17955.561660766602), static_cast(-9.6968994205831605e-09), static_cast(-82.406154185533524), SC_(-6.98055306629610746072607353939306734740549551e-11) }}, { { static_cast(-17955.561660766602), static_cast(-9.6968994205831605e-09), static_cast(82.406154185533524), SC_(-42897094853118832762870100.8669248353530950866) }} , @@ -287,6 +287,11 @@ void test_spots6(T, const char* type_name) { { (T)std::ldexp((double)-9751199809536000, -45), (T)std::ldexp((double)-17654191685632000, -47), (T)std::ldexp((double)10587451850752000, -47), SC_(-1.9601415510439595625538337964298353914980331018955e+68) }}, { { (T)std::ldexp((double)-15233620754432000, -45), (T)std::ldexp((double)-12708283072512000, -46), (T)std::ldexp((double)10255461007360000, -46), SC_(-5.4344106361679075861859567858016187271235441673635e+125) }}, { { (T)std::ldexp((double)-11241354149888000, -45), (T)std::ldexp((double)-9580579905536000, -45), (T)std::ldexp((double)12224976846848000, -47), SC_(12046856548470067405870726490464935201150430438.035) }}, + // + // Bugs found while testing color maps: + // + { { SC_(0.078125000000000000), SC_(-0.039062500000000000), SC_(0.5), SC_(-0.3371910410915676603577770246237158427221) }}, + { { SC_(-19.492187500000000), SC_(0.50781250000000000), SC_(0.5), SC_(1.2551298228307647570646714060395253358015) }}, } }; static const std::array, 2> hypergeometric_1F1_big_bugs = { { #if DBL_MAX_EXP == LDBL_MAX_EXP From 5da27935f2c36bf650ec5727b263438babf0780b Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 10 Mar 2022 19:33:51 +0000 Subject: [PATCH 19/60] Added further color table example. --- doc/images/extended_kindlmann_1F1.png | Bin 0 -> 305902 bytes doc/internals/color_maps.qbk | 7 +- example/color_maps_sf_example.cpp | 606 ++++++++++++++++++++++++++ 3 files changed, 612 insertions(+), 1 deletion(-) create mode 100644 doc/images/extended_kindlmann_1F1.png create mode 100644 example/color_maps_sf_example.cpp diff --git a/doc/images/extended_kindlmann_1F1.png b/doc/images/extended_kindlmann_1F1.png new file mode 100644 index 0000000000000000000000000000000000000000..5780e2a8c4ad3c3f26bf664e1c1e060dafa5d4cc GIT binary patch literal 305902 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\GL>XA=_MtuOg(sK)M6`+6-sghEh5d{EAW5X> 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 k;s5_76zBg3aacPcYv62(Pxy7!96F +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !__has_include("lodepng.h") + #error "lodepng.h is required to run this example." +#endif +#include "lodepng.h" +#include +#include +#include +#include + +// In lodepng, the vector is expected to be row major, with the top row +// specified first. Note that this is a bit confusing sometimes as it's more +// natural to let y increase moving *up*. +unsigned write_png(const std::string& filename, + const std::vector& img, std::size_t width, + std::size_t height) { + unsigned error = lodepng::encode(filename, img, width, height, + LodePNGColorType::LCT_RGBA, 8); + if (error) { + std::cerr << "Error encoding png: " << lodepng_error_text(error) << "\n"; + } + return error; +} + +double hypergeometric_1F1_at_half(double x, double y) +{ + try + { + return boost::math::hypergeometric_1F1(x, y, -3.5); + } + catch (const std::domain_error&) + { + return 0; + } +} + +void show_help() +{ + std::cout << + "The following command line options are supported:\n" + " gamma_p|gamma_q|gamma_p_inv|gamma_q_inv|cyl_bessel_j|cyl_neumann|cyl_bessel_i|cyl_bessel_k\n" + " |cyl_bessel_d|ellint_1|ellint_2|ellint_3|jacobi_zeta|heuman_lambda|jacobi_theta1|1F1\n" + " Sets the function to be plotted.\n" + " Note that the defaults for the options below change depending on the function selected here,\n" + " so set this option first, and then fine tune with the following options:\n" + " smooth_cool_warm|plasma|black_body|inferno|kindlmann|extended_kindlmann\n" + " Sets the color map used.\n" + " width=XX\n" + " height=XX\n" + " Sets the width and height of the bitmap.\n" + " x_min=XX\n" + " x_max=XX\n" + " y_min=XX\n" + " y_max=XX\n" + " Sets the extent of the x and y variables passed to the function.\n" + " log=false|true|0|1\n" + " Turns logarithmic scale on or off (default off)\n"; +} + +int main(int argc, char** argv) +{ + using Real = double; + using boost::math::tools::viridis; + using std::sqrt; + + std::function(Real)> color_map = viridis; + std::string requested_color_map = "viridis"; + std::string function_name = "gamma_p"; + int64_t image_width = 1024; + int64_t image_height = 1024; + + double x_min{ 0.001 }, x_max{ 20 }; + double y_min{ 0.001 }, y_max{ 20 }; + + Real(*the_function)(Real, Real) = boost::math::gamma_p; + bool log_scale = false; + bool debug = false; + + for(unsigned i = 1; i < argc; ++i) + { + std::string arg = std::string(argv[i]); + if (arg == "smooth_cool_warm") { + requested_color_map = arg; + color_map = boost::math::tools::smooth_cool_warm; + } + else if (arg == "plasma") { + requested_color_map = arg; + color_map = boost::math::tools::plasma; + } + else if (arg == "black_body") { + requested_color_map = arg; + color_map = boost::math::tools::black_body; + } + else if (arg == "inferno") { + requested_color_map = arg; + color_map = boost::math::tools::inferno; + } + else if (arg == "kindlmann") { + requested_color_map = arg; + color_map = boost::math::tools::kindlmann; + } + else if (arg == "extended_kindlmann") { + requested_color_map = arg; + color_map = boost::math::tools::extended_kindlmann; + } + else if (arg.compare(0, 6, "width=") == 0) + { + image_width = std::strtol(arg.c_str() + 6, nullptr, 10); + } + else if (arg.compare(0, 7, "height=") == 0) + { + image_height = std::strtol(arg.c_str() + 7, nullptr, 10); + } + else if (arg.compare(0, 6, "x_min=") == 0) + { + x_min = std::strtod(arg.c_str() + 6, nullptr); + } + else if (arg.compare(0, 6, "x_max=") == 0) + { + x_max = std::strtod(arg.c_str() + 6, nullptr); + } + else if (arg.compare(0, 6, "y_min=") == 0) + { + y_min = std::strtod(arg.c_str() + 6, nullptr); + } + else if (arg.compare(0, 6, "y_max=") == 0) + { + y_max = std::strtod(arg.c_str() + 6, nullptr); + } + else if (arg == "log=1") + { + log_scale = true; + } + else if (arg == "log=0") + { + log_scale = false; + } + else if (arg == "log=true") + { + log_scale = true; + } + else if (arg == "log=false") + { + log_scale = false; + } + else if (arg == "debug") + { + debug = true; + } + else if (arg == "gamma_p") + { + the_function = boost::math::gamma_p; + function_name = arg; + if (requested_color_map == "viridis") + { + std::cout << "Setting default gamma_p color map to extended_kindlmann" << std::endl; + requested_color_map = "extended_kindlmann"; + color_map = boost::math::tools::extended_kindlmann; + } + } + else if (arg == "gamma_q") + { + the_function = boost::math::gamma_q; + function_name = arg; + if (requested_color_map == "viridis") + { + std::cout << "Setting default gamma_p color map to extended_kindlmann" << std::endl; + requested_color_map = "extended_kindlmann"; + color_map = boost::math::tools::extended_kindlmann; + } + } + else if (arg == "gamma_p_inv") + { + the_function = boost::math::gamma_p_inv; + function_name = arg; + if (y_max > 1) + { + std::cout << "Setting y range to [0.01, 0.99] for gamma_p_inv" << std::endl; + y_min = 0.01; + y_max = 0.99; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default gamma_p_inv color map to inferno" << std::endl; + requested_color_map = "inferno"; + color_map = boost::math::tools::inferno; + } + } + else if (arg == "gamma_q_inv") + { + the_function = boost::math::gamma_q_inv; + function_name = arg; + if (y_max > 1) + { + std::cout << "Setting y range to [0.01, 0.99] for gamma_p_inv" << std::endl; + y_min = 0.01; + y_max = 0.99; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default gamma_p_inv color map to inferno" << std::endl; + requested_color_map = "inferno"; + color_map = boost::math::tools::inferno; + } + } + else if (arg == "beta") + { + the_function = boost::math::beta; + function_name = arg; + if (log_scale == false) + { + std::cout << "Setting log scale to true for beta" << std::endl; + log_scale = true; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default beta color map to smooth_cool_warm" << std::endl; + requested_color_map = "smooth_cool_warm"; + color_map = boost::math::tools::smooth_cool_warm; + } + } + else if (arg == "cyl_bessel_j") + { + the_function = boost::math::cyl_bessel_j; + function_name = arg; + if (requested_color_map == "viridis") + { + std::cout << "Setting default beta color map to smooth_cool_warm" << std::endl; + requested_color_map = "smooth_cool_warm"; + color_map = boost::math::tools::smooth_cool_warm; + } + } + else if (arg == "cyl_neumann") + { + the_function = boost::math::cyl_neumann; + function_name = arg; + if (requested_color_map == "viridis") + { + std::cout << "Setting default cyl_neumann color map to black_body" << std::endl; + requested_color_map = "black_body"; + color_map = boost::math::tools::black_body; + } + if (x_max > 1.5) + { + std::cout << "Setting cyl_neumann default x range to [0.5,1.5]" << std::endl; + x_min = 0.5; + x_max = 1.5; + } + if (log_scale == false) + { + std::cout << "Turning on log scale for cyl_neumann" << std::endl; + log_scale = true; + } + } + else if (arg == "cyl_bessel_i") + { + the_function = boost::math::cyl_bessel_i; + function_name = arg; + if (requested_color_map == "viridis") + { + std::cout << "Setting default cyl_bessel_i color map to black_body" << std::endl; + requested_color_map = "black_body"; + color_map = boost::math::tools::black_body; + } + if (x_max > 1.5) + { + std::cout << "Setting cyl_bessel_i default x range to [0.5,1.5]" << std::endl; + x_min = 0.5; + x_max = 1.5; + } + if (log_scale == false) + { + std::cout << "Turning on log scale for cyl_bessel_i" << std::endl; + log_scale = true; + } + } + else if (arg == "cyl_bessel_k") + { + the_function = boost::math::cyl_bessel_k; + function_name = arg; + if (requested_color_map == "viridis") + { + std::cout << "Setting default cyl_bessel_k color map to plasma" << std::endl; + requested_color_map = "plasma"; + color_map = boost::math::tools::plasma; + } + if (x_max > 1.5) + { + std::cout << "Setting cyl_bessel_k default x range to [0,5]" << std::endl; + x_min = 0.01; + x_max = 5; + } + if (log_scale == false) + { + std::cout << "Turning on log scale for cyl_bessel_k" << std::endl; + log_scale = true; + } + } + else if (arg == "cyl_bessel_d") + { + the_function = boost::math::cyl_bessel_k; + function_name = arg; + if (log_scale == false) + { + std::cout << "Turning on log scale for cyl_bessel_d" << std::endl; + log_scale = true; + } + } + else if (arg == "ellint_1") + { + the_function = boost::math::ellint_1; + function_name = arg; + // x_max=1 y_max=1.5 kindlmann log=true + if (x_max >= 20) + { + std::cout << "Setting ellint_1 x range to [0, 1]" << std::endl; + x_max = 1; + } + if (y_max >= 20) + { + std::cout << "Setting ellint_1 y range to [0, 1.5]" << std::endl; + x_max = 1.5; + } + if (log_scale == false) + { + std::cout << "Turning on log scale for ellint_1" << std::endl; + log_scale = true; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default ellint_1 color map to kindlmann" << std::endl; + requested_color_map = "kindlmann"; + color_map = boost::math::tools::kindlmann; + } + } + else if (arg == "ellint_2") + { + the_function = boost::math::ellint_2; + function_name = arg; + // x_max=1 y_max=1.5 kindlmann log=true + if (x_max >= 20) + { + std::cout << "Setting ellint_2 x range to [-1, 1]" << std::endl; + x_max = 1; + x_min = -1; + } + if (y_max >= 20) + { + std::cout << "Setting ellint_2 y range to [0, 1.5]" << std::endl; + y_max = 1.5; + } + if (log_scale == false) + { + std::cout << "Turning on log scale for ellint_2" << std::endl; + log_scale = true; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default ellint_1 color map to kindlmann" << std::endl; + requested_color_map = "kindlmann"; + color_map = boost::math::tools::kindlmann; + } + } + else if (arg == "ellint_3") + { + the_function = boost::math::ellint_3; + function_name = arg; + // x_max=1 y_max=1.5 kindlmann log=true + if (x_max >= 20) + { + std::cout << "Setting ellint_3 x range to [-0.99, 0.99]" << std::endl; + x_max = 0.99; + x_min = -0.99; + } + if (y_max >= 20) + { + std::cout << "Setting ellint_3 y range to [0, 1]" << std::endl; + y_max = 1; + } + if (log_scale == false) + { + std::cout << "Turning on log scale for ellint_3" << std::endl; + log_scale = true; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default ellint_3 color map to kindlmann" << std::endl; + requested_color_map = "kindlmann"; + color_map = boost::math::tools::kindlmann; + } + } + else if (arg == "jacobi_zeta") + { + the_function = boost::math::jacobi_zeta; + function_name = arg; + // x_max=1 y_max=1.5 kindlmann log=true + if (x_max >= 20) + { + std::cout << "Setting jacobi_zeta x range to [-0.99, 0.99]" << std::endl; + x_max = 0.99; + x_min = -0.99; + } + if (y_max >= 20) + { + std::cout << "Setting jacobi_zeta y range to [0, 1]" << std::endl; + y_max = 0.99; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default jacobi_zeta color map to kindlmann" << std::endl; + requested_color_map = "kindlmann"; + color_map = boost::math::tools::kindlmann; + } + } + else if (arg == "heuman_lambda") + { + the_function = boost::math::heuman_lambda; + function_name = arg; + // x_max=1 y_max=1.5 kindlmann log=true + if (x_max >= 20) + { + std::cout << "Setting heuman_lambda x range to [-0.99, 0.99]" << std::endl; + x_max = 0.99; + x_min = -0.99; + } + if (y_max >= 20) + { + std::cout << "Setting heuman_lambda y range to [0, 1]" << std::endl; + y_max = 0.99; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default heuman_lambda color map to kindlmann" << std::endl; + requested_color_map = "kindlmann"; + color_map = boost::math::tools::kindlmann; + } + } + else if (arg == "jacobi_theta1") + { + the_function = boost::math::jacobi_theta1; + function_name = arg; + if (y_max >= 20) + { + std::cout << "Setting jacobi_theta1 y range to [0, 1]" << std::endl; + y_max = 0.99; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default jacobi_theta1 color map to kindlmann" << std::endl; + requested_color_map = "kindlmann"; + color_map = boost::math::tools::kindlmann; + } + } + else if (arg == "1F1") + { + the_function = hypergeometric_1F1_at_half; + function_name = arg; + if (x_min >= 0) + { + std::cout << "Setting 1F1 x range to [-20,20]" << std::endl; + x_min = -20; + x_max = 20; + } + if (y_min >= 0) + { + std::cout << "Setting 1F1 y range to [-20,20]" << std::endl; + y_min = -20; + y_max = 20; + } + if (requested_color_map == "viridis") + { + std::cout << "Setting default 1F1 color map to extended_kindlmann" << std::endl; + requested_color_map = "extended_kindlmann"; + color_map = boost::math::tools::extended_kindlmann; + } + if (!log_scale) + { + std::cout << "Turning on logarithmic scale for 1F1" << std::endl; + log_scale = true; + } + } + else if (arg == "help") + { + show_help(); + return 0; + } + else + { + std::cerr << "Could not recognize argument " << argv[i] << ".\n\n"; + show_help(); + return 1; + } + } + + std::vector img(4*image_width*image_height, 0); + std::vector points(image_width*image_height, 0); + + Real min_value{ std::numeric_limits::infinity() }, max_value{ -std::numeric_limits::infinity() }; + // + // Get a matrix of points: + // + for (int64_t i = 0; i < image_width; ++i) + { + for (int64_t j = 0; j < image_height; ++j) + { + double x = x_max - (image_width - i) * (x_max - x_min) / image_width; + double y = y_max - (image_height - j) * (y_max - y_min) / image_height; + + Real p = the_function(x, y); + if (std::isnan(p)) + std::cerr << "Ooops, got a NaN" << std::endl; + if (p < min_value) + min_value = p; + if (p > max_value) + max_value = p; + points[i + image_width * (image_height - j - 1)] = p; + } + } + std::cout << "Function range is: [" << std::setprecision(3) << min_value << "," << max_value << "]\n"; + // + // Handle log scale, the formula differs depending on whether we have found negative values or not: + // + if (log_scale) + { + Real new_max = -std::numeric_limits::infinity(); + Real new_min = std::numeric_limits::infinity(); + for (int64_t i = 0; i < points.size(); ++i) + { + Real p = points[i]; + if (min_value <= 0) + p = boost::math::sign(p) * log10(1 + std::fabs(p * boost::math::constants::ln_ten())); + else + p = log(p); + if (std::isnan(p)) + std::cerr << "Ooops, got a NaN" << std::endl; + if (p < new_min) + new_min = p; + if (p > new_max) + new_max = p; + points[i] = p; + } + max_value = new_max; + min_value = new_min; + std::cout << "Function range is: [" << std::setprecision(3) << min_value << "," << max_value << "]\n"; + } + + // + // Normalize the points so they are all in [0,1] + // + for (int64_t i = 0; i < points.size(); ++i) + { + double p = points[i]; + p -= min_value; + p /= (max_value - min_value); + points[i] = p; + } + // + // debugging, adds an alternating 0 and 1 row on the second half of the zeroth row: + // + if (debug) + { + for (int64_t i = image_width / 2; i < image_width; ++i) + points[image_width * (image_height - 1) + i] = i & 1 ? 1 : 0; + } + + // + // Now calculate the RGB bitmap from the points: + // + for (int64_t i = 0; i < image_width; ++i) + { + for (int64_t j = 0; j < image_height; ++j) + { + double p = points[i + image_width * j]; + auto c = boost::math::tools::to_8bit_rgba(color_map(p)); + int64_t idx = 4 * (image_width * j + i); + img[idx + 0] = c[0]; + img[idx + 1] = c[1]; + img[idx + 2] = c[2]; + img[idx + 3] = c[3]; + } + } + + // Requires lodepng.h + // See: https://github.com/lvandeve/lodepng for download and compilation instructions + write_png(requested_color_map + "_" + function_name + ".png", img, image_width, image_height); +} From bd7fcf1545dbf986fe08c913454daa2a49e72371 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 11 Mar 2022 10:40:24 +0000 Subject: [PATCH 20/60] Correct conceptual usage of RealType. --- .../special_functions/detail/hypergeometric_1F1_recurrence.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp b/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp index 319ce76795..07cb236db1 100644 --- a/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp +++ b/include/boost/math/special_functions/detail/hypergeometric_1F1_recurrence.hpp @@ -296,7 +296,7 @@ // When ak - 1 == b are recursion coefficients dissappear to zero and // we end up with a NaN result. Reduce the recursion steps by 1 to // avoid this. We rely on |b| small and therefore no infinite recursion. - --ak; + ak -= 1; integer_part += 1; } From 4a4985d47a11bbe629e97aabb190f1dd0c0063e1 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 12 Mar 2022 10:20:01 +0000 Subject: [PATCH 21/60] Fix logic error in roots.hpp Fixes https://github.com/boostorg/math/issues/776 --- include/boost/math/tools/roots.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/boost/math/tools/roots.hpp b/include/boost/math/tools/roots.hpp index 5b06e5bc97..88685faad8 100644 --- a/include/boost/math/tools/roots.hpp +++ b/include/boost/math/tools/roots.hpp @@ -372,6 +372,8 @@ namespace detail { T bracket_root_towards_max(F f, T guess, const T& f0, T& min, T& max, std::uintmax_t& count) noexcept(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { using std::fabs; + if(count < 2) + return guess0 - (max + min) / 2; // Not enough counts left to do anything!! // // Move guess towards max until we bracket the root, updating min and max as we go: // @@ -427,6 +429,8 @@ namespace detail { T bracket_root_towards_min(F f, T guess, const T& f0, T& min, T& max, std::uintmax_t& count) noexcept(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { using std::fabs; + if (count < 2) + return guess0 - (max + min) / 2; // Not enough counts left to do anything!! // // Move guess towards min until we bracket the root, updating min and max as we go: // From cf1c5c95b832208153d87a64081d8e44d9ad4563 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 12 Mar 2022 15:42:12 +0000 Subject: [PATCH 22/60] Correct variable name typo. --- include/boost/math/tools/roots.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/math/tools/roots.hpp b/include/boost/math/tools/roots.hpp index 88685faad8..0f2129d1f7 100644 --- a/include/boost/math/tools/roots.hpp +++ b/include/boost/math/tools/roots.hpp @@ -373,7 +373,7 @@ namespace detail { { using std::fabs; if(count < 2) - return guess0 - (max + min) / 2; // Not enough counts left to do anything!! + return guess - (max + min) / 2; // Not enough counts left to do anything!! // // Move guess towards max until we bracket the root, updating min and max as we go: // @@ -430,7 +430,7 @@ namespace detail { { using std::fabs; if (count < 2) - return guess0 - (max + min) / 2; // Not enough counts left to do anything!! + return guess - (max + min) / 2; // Not enough counts left to do anything!! // // Move guess towards min until we bracket the root, updating min and max as we go: // From f0b818a6d0f945a461513db2f3541755c4ec9465 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 12 Mar 2022 16:02:18 +0000 Subject: [PATCH 23/60] Regenerate docs, add missing files. [CI SKIP] --- doc/html/index.html | 2 +- doc/html/indexes/s01.html | 2 +- doc/html/indexes/s02.html | 2 +- doc/html/indexes/s03.html | 2 +- doc/html/indexes/s04.html | 2 +- doc/html/indexes/s05.html | 2 +- doc/html/math_toolkit/conventions.html | 2 +- .../math_toolkit/internals/color_maps.html | 170 ++++++++++++++++++ doc/html/math_toolkit/navigation.html | 2 +- doc/html/math_toolkit/powers/logaddexp.html | 166 +++++++++++++++++ doc/html/math_toolkit/quartic_roots.html | 88 +++++++++ 11 files changed, 432 insertions(+), 8 deletions(-) create mode 100644 doc/html/math_toolkit/internals/color_maps.html create mode 100644 doc/html/math_toolkit/powers/logaddexp.html create mode 100644 doc/html/math_toolkit/quartic_roots.html diff --git a/doc/html/index.html b/doc/html/index.html index 42df72f19c..8d51e7ffed 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -134,7 +134,7 @@ - +

Last revised: March 08, 2022 at 16:03:29 GMT

Last revised: March 12, 2022 at 15:44:15 GMT


diff --git a/doc/html/indexes/s01.html b/doc/html/indexes/s01.html index 616dcb9112..9565662656 100644 --- a/doc/html/indexes/s01.html +++ b/doc/html/indexes/s01.html @@ -24,7 +24,7 @@

1 2 4 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

diff --git a/doc/html/indexes/s02.html b/doc/html/indexes/s02.html index 0580ea017c..cc8984f38d 100644 --- a/doc/html/indexes/s02.html +++ b/doc/html/indexes/s02.html @@ -24,7 +24,7 @@

-Class Index

+Class Index

A B C D E F G H I K L M N O P Q R S T U V W

diff --git a/doc/html/indexes/s03.html b/doc/html/indexes/s03.html index f9cd0c9105..b656b949f4 100644 --- a/doc/html/indexes/s03.html +++ b/doc/html/indexes/s03.html @@ -24,7 +24,7 @@

-Typedef Index

+Typedef Index

A B C D E F G H I K L N P R S T U V W

diff --git a/doc/html/indexes/s04.html b/doc/html/indexes/s04.html index 4f65f61b4f..3c5f6a5f41 100644 --- a/doc/html/indexes/s04.html +++ b/doc/html/indexes/s04.html @@ -24,7 +24,7 @@

-Macro Index

+Macro Index

B F

diff --git a/doc/html/indexes/s05.html b/doc/html/indexes/s05.html index 9fba74a987..e808a68a15 100644 --- a/doc/html/indexes/s05.html +++ b/doc/html/indexes/s05.html @@ -23,7 +23,7 @@

-Index

+Index

1 2 4 5 7 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

diff --git a/doc/html/math_toolkit/conventions.html b/doc/html/math_toolkit/conventions.html index e725bfc12d..11f88a46b7 100644 --- a/doc/html/math_toolkit/conventions.html +++ b/doc/html/math_toolkit/conventions.html @@ -27,7 +27,7 @@ Document Conventions

- +

This documentation aims to use of the following naming and formatting conventions. diff --git a/doc/html/math_toolkit/internals/color_maps.html b/doc/html/math_toolkit/internals/color_maps.html new file mode 100644 index 0000000000..febbba1212 --- /dev/null +++ b/doc/html/math_toolkit/internals/color_maps.html @@ -0,0 +1,170 @@ + + + +Color Maps + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+


+
+PrevUpHomeNext +
+
+ +
+ + Synopsis +
+
#include <boost/math/tools/color_maps.hpp>
+
+namespace boost::math::tools {
+
+template<typename Real>
+std::array<Real, 3> viridis(Real x);
+
+template<typename Real>
+std::array<Real, 3> plasma(Real x);
+
+template<typename Real>
+std::array<Real, 3> black_body(Real x);
+
+template<typename Real>
+std::array<Real, 3> inferno(Real x);
+
+template<typename Real>
+std::array<Real, 3> smooth_cool_warm(Real x);
+
+template<typename Real>
+std::array<Real, 3> kindlmann(Real x);
+
+template<typename Real>
+std::array<Real, 3> extended_kindlmann(Real x);
+
+template<typename Real>
+std::array<uint8_t, 4> to_8bit_rgba(std::array<Real, 3> const & color);
+
+} // namespaces
+
+
+ + Description +
+

+ Abstractly, a color map is any function which maps [0, 1] -> [0, 1]^3. + As stated, this definition is too broad to be useful, so in Boost, we restrict + our attention to the subset of color maps which are useful for the understanding + of scientific data. Much + research has demonstrated that color maps differ wildly in their + usefulness for interpreting quantitative data; see here + for details. In addition, different color maps are useful in different contexts. + For example, the smooth_cool_warm + color map is useful for examining surfaces embedded in 3-space which have + scalar fields defined on them, whereas the inferno + color map is better for understanding 2D data. +

+

+ Despite the fact that a color map, per our definition, has a domain of [0, + 1], we nonetheless do not throw an exception if the value provided falls + outside this range. This is for two reasons: First, visualizations are themselves + amazing debuggers, and if we threw an exception the calculation would not + complete and visual debugging would be inaccessible. Second, often small + changes in floating point rounding cause the value provided to be only slightly + below zero, or just slightly above 1. Hence, we make a call to std::clamp + before interpolating into the color table. +

+

+ For an example of how to use these facilites please refer to example/color_maps_example.cpp Note: To compile the examples directly + you will need to have lodepng. + An example of the viridis color map using the + newton fractal is shown below: +

+

+ +

+

+ Swatches of each are listed below: +

+

+ +

+
  • + Smooth cool warm* +
+

+ +

+
  • + Viridis* +
+

+ +

+
  • + Plasma* +
+

+ +

+
  • + Black body* +
+

+ +

+
  • + Inferno* +
+

+ +

+
  • + Kindlmann* +
+

+ +

+
  • + Extended Kindlmann* +
+
+ + References +
+
  • + Ken Moreland. Why We Use Bad Color Maps and What You Can Do + About it . +
+
+ + + +
+
+
+PrevUpHomeNext +
+ + diff --git a/doc/html/math_toolkit/navigation.html b/doc/html/math_toolkit/navigation.html index a3f71c46c9..c874d6c9a8 100644 --- a/doc/html/math_toolkit/navigation.html +++ b/doc/html/math_toolkit/navigation.html @@ -27,7 +27,7 @@ Navigation

- +

Boost.Math documentation is provided in both HTML and PDF formats. diff --git a/doc/html/math_toolkit/powers/logaddexp.html b/doc/html/math_toolkit/powers/logaddexp.html new file mode 100644 index 0000000000..774cac6606 --- /dev/null +++ b/doc/html/math_toolkit/powers/logaddexp.html @@ -0,0 +1,166 @@ + + + +logaddexp and logsumexp + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+


+
+PrevUpHomeNext +
+
+ +
+ + Synopsis +
+
#include <boost/math/special_functions/logaddexp.hpp>
+
+namespace boost { namespace math {
+
+template <typename Real>
+Real logaddexp (Real x1, Real x2);
+
+}} // namespaces
+
+#include <boost/math/special_functions/logsumexp.hpp>
+
+namespace boost { namespace math {
+
+template <typename ForwardIterator, typename Real = std::iterator_traits<ForwardIterator>::value_type>
+Real logsumexp (ForwardIterator first, ForwardIterator last);
+
+template <typename ForwardContainer, typename Real = ForwardContainer::value_type>
+Real logsumexp (const ForwardContainer& c);
+
+template <typename... Args, typename Real = typename std::common_type<Args...>::type>
+Real logsumexp(Args&& ...args)
+
+}} // namespace
+
+

+ The function logaddexp(x1, x2) computes log(exp(x1) + exp(x2)). The function + logsumexp(x1, x2, ...) is + generalized to compute log(exp(x1) + exp(x2) + ...). This function is useful + in statistics where the calculated probabilities of events may be so small + as to exceed the range of normal floating point numbers. In such cases the + logarithm of the calculated probability is stored. +

+

+ The use is +

+
using std::log;
+
+double x1 = log(1e-50);
+double x2 = log(2.5e-50);
+double x3 = log(3e-50);
+std::vector<double> x = {x1, x2, x3};
+
+double probability1 = boost::math::logaddexp(x1, x2);
+double probability2 = boost::math::logsumexp(x1, x2, x3);
+double probability3 = boost::math::logsumexp(x);
+double probability4 = boost::math::logsumexp(x.begin(), x.end());
+
+

+ Performance Data: +

+
Running ./logaddexp_performance
+Run on Apple M1 Pro
+CPU Caches:
+  L1 Data 64 KiB (x10)
+  L1 Instruction 128 KiB (x10)
+  L2 Unified 4096 KiB (x5)
+Load Average: 2.23, 1.89, 1.88
+-----------------------------------------------------------------------------
+Benchmark                                   Time             CPU   Iterations
+-----------------------------------------------------------------------------
+logaddexp_performance<float>             1.05 ns         1.05 ns    597983940
+logaddexp_performance<double>            1.03 ns         1.03 ns    672682369
+
+
Running ./logsumexp_performance
+Run on Apple M1 Pro
+CPU Caches:
+  L1 Data 64 KiB (x10)
+  L1 Instruction 128 KiB (x10)
+  L2 Unified 4096 KiB (x5)
+Load Average: 1.56, 1.67, 1.81
+-----------------------------------------------------------------------------------------------
+Benchmark                                                     Time             CPU   Iterations
+-----------------------------------------------------------------------------------------------
+logsumexp_performance<float>/64/real_time                   388 ns          388 ns      1797191
+logsumexp_performance<float>/128/real_time                  761 ns          761 ns       890017
+logsumexp_performance<float>/256/real_time                 1513 ns         1513 ns       460217
+logsumexp_performance<float>/512/real_time                 3026 ns         3026 ns       231454
+logsumexp_performance<float>/1024/real_time                6043 ns         6043 ns       113901
+logsumexp_performance<float>/2048/real_time               12084 ns        12084 ns        57590
+logsumexp_performance<float>/4096/real_time               24240 ns        24240 ns        28835
+logsumexp_performance<float>/8192/real_time               48326 ns        48323 ns        14478
+logsumexp_performance<float>/16384/real_time              96645 ns        96642 ns         7181
+logsumexp_performance<float>/32768/real_time             193351 ns       193351 ns         3607
+logsumexp_performance<float>/65536/real_time             386537 ns       386538 ns         1810
+logsumexp_performance<float>/131072/real_time            774055 ns       774056 ns          894
+logsumexp_performance<float>/262144/real_time           1548913 ns      1548847 ns          451
+logsumexp_performance<float>/524288/real_time           3092771 ns      3092770 ns          226
+logsumexp_performance<float>/1048576/real_time          6188087 ns      6188089 ns          112
+logsumexp_performance<float>/real_time_BigO                5.90 N          5.90 N
+logsumexp_performance<float>/real_time_RMS                    0 %             0 %
+logsumexp_performance<double>/64/real_time                  388 ns          388 ns      1806669
+logsumexp_performance<double>/128/real_time                 770 ns          770 ns       898340
+logsumexp_performance<double>/256/real_time                1534 ns         1534 ns       454768
+logsumexp_performance<double>/512/real_time                3063 ns         3063 ns       228057
+logsumexp_performance<double>/1024/real_time               6126 ns         6126 ns       112667
+logsumexp_performance<double>/2048/real_time              12243 ns        12243 ns        56963
+logsumexp_performance<double>/4096/real_time              24476 ns        24476 ns        28485
+logsumexp_performance<double>/8192/real_time              48979 ns        48978 ns        14215
+logsumexp_performance<double>/16384/real_time             97929 ns        97929 ns         7070
+logsumexp_performance<double>/32768/real_time            195826 ns       195826 ns         3560
+logsumexp_performance<double>/65536/real_time            391855 ns       391835 ns         1787
+logsumexp_performance<double>/131072/real_time           784119 ns       784110 ns          882
+logsumexp_performance<double>/262144/real_time          1566408 ns      1566386 ns          446
+logsumexp_performance<double>/524288/real_time          3151649 ns      3150955 ns          223
+logsumexp_performance<double>/1048576/real_time         6300578 ns      6299027 ns          110
+logsumexp_performance<double>/real_time_BigO               6.01 N          6.01 N
+
+
+ + References +
+
+
+ + + +
+
+
+PrevUpHomeNext +
+ + diff --git a/doc/html/math_toolkit/quartic_roots.html b/doc/html/math_toolkit/quartic_roots.html new file mode 100644 index 0000000000..8cccd2d1ab --- /dev/null +++ b/doc/html/math_toolkit/quartic_roots.html @@ -0,0 +1,88 @@ + + + +Roots of Quartic Polynomials + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+ +

+ + Synopsis +

+
#include <boost/math/roots/quartic_roots.hpp>
+
+namespace boost::math::tools {
+
+// Solves axⴠ+ bx³ + cx² + dx + e = 0.
+std::array<Real,3> quartic_roots(Real a, Real b, Real c, Real d, Real e);
+
+}
+
+

+ + Background +

+

+ The quartic_roots function + extracts all real roots of a quartic polynomial axâ´+ bx³ + cx² + dx + e. + The result is a std::array<Real, 4>, which has length four, irrespective of + the number of real roots the polynomial possesses. (This is to prevent the + performance overhead of allocating a vector, which often exceeds the time to + extract the roots.) The roots are returned in nondecreasing order. If a root + is complex, then it is placed at the back of the array and set to a nan. +

+

+ The algorithm uses the classical method of Ferrari, and follows Graphics + Gems V, with an additional Halley iterate for root polishing. A typical + use of a quartic real-root solver is to raytrace a torus. +

+

+ + Performance + and Accuracy +

+

+ On a consumer laptop, we observe extraction of the roots taking ~90ns. The + file reporting/performance/quartic_roots_performance.cpp allows determination of the speed on + your system. +

+
+ + + +
+
+
+PrevUpHomeNext +
+ + From 222b81e01934eb2ee61c4fcceaa215fbcae05582 Mon Sep 17 00:00:00 2001 From: Warren Weckesser Date: Wed, 13 Apr 2022 15:42:51 -0400 Subject: [PATCH 24/60] Fix repeated name in example/policy_eg_1.cpp --- example/policy_eg_1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/policy_eg_1.cpp b/example/policy_eg_1.cpp index 7f8d9fbb44..01f2f2797c 100644 --- a/example/policy_eg_1.cpp +++ b/example/policy_eg_1.cpp @@ -21,7 +21,7 @@ using boost::math::policies::policy; using boost::math::policies::evaluation_error; using boost::math::policies::domain_error; using boost::math::policies::overflow_error; -using boost::math::policies::domain_error; +using boost::math::policies::underflow_error; using boost::math::policies::pole_error; // Actions on error (in enum error_policy_type): using boost::math::policies::errno_on_error; From 209ad4755acccea91484646d589e17d59f510a7a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Sat, 16 Apr 2022 14:26:42 -0700 Subject: [PATCH 25/60] Fixes from review and Clang-Tidy/SonarLint --- .gitignore | 2 + .../boost/math/distributions/chi_squared.hpp | 79 ++----------------- .../boost/math/distributions/exponential.hpp | 14 ++-- .../math/distributions/extreme_value.hpp | 19 ++--- include/boost/math/distributions/gamma.hpp | 12 +-- .../math/distributions/inverse_gamma.hpp | 25 +++--- .../math/distributions/inverse_gaussian.hpp | 67 +++++----------- include/boost/math/distributions/laplace.hpp | 36 +++++---- include/boost/math/distributions/normal.hpp | 43 +++------- include/boost/math/distributions/poisson.hpp | 39 ++++----- include/boost/math/distributions/rayleigh.hpp | 31 +++----- include/boost/math/distributions/weibull.hpp | 39 +++++---- 12 files changed, 140 insertions(+), 266 deletions(-) diff --git a/.gitignore b/.gitignore index 901283a3f7..2b74895e20 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ Makefile **CTestTestfile.cmake DartConfiguration.tcl cmake-build-debug/* +.cmake/* +build.ninja diff --git a/include/boost/math/distributions/chi_squared.hpp b/include/boost/math/distributions/chi_squared.hpp index a81197a851..7b65a0da68 100644 --- a/include/boost/math/distributions/chi_squared.hpp +++ b/include/boost/math/distributions/chi_squared.hpp @@ -23,10 +23,10 @@ template > class chi_squared_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - chi_squared_distribution(RealType i) : m_df(i) + explicit chi_squared_distribution(RealType i) : m_df(i) { RealType result; detail::check_df( @@ -53,7 +53,7 @@ class chi_squared_distribution RealType m_df; // degrees of freedom is a positive real number. }; // class chi_squared_distribution -typedef chi_squared_distribution chi_squared; +using chi_squared = chi_squared_distribution; #ifdef __cpp_deduction_guides template @@ -66,7 +66,7 @@ chi_squared_distribution(RealType)->chi_squared_distribution -inline const std::pair range(const chi_squared_distribution& /*dist*/) +inline std::pair range(const chi_squared_distribution& /*dist*/) { // Range of permissible values for random variable x. if (std::numeric_limits::has_infinity) { @@ -84,7 +84,7 @@ inline const std::pair range(const chi_squared_distribution< #endif template -inline const std::pair support(const chi_squared_distribution& /*dist*/) +inline std::pair support(const chi_squared_distribution& /*dist*/) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. return std::pair(static_cast(0), tools::max_value()); // 0 to + infinity. @@ -131,47 +131,6 @@ RealType pdf(const chi_squared_distribution& dist, const RealT return gamma_p_derivative(degrees_of_freedom / 2, chi_square / 2, Policy()) / 2; } // pdf -template -RealType logpdf(const chi_squared_distribution& dist, const RealType& chi_square) -{ - BOOST_MATH_STD_USING // for ADL of std functions - RealType k = dist.degrees_of_freedom(); - // Error check: - RealType error_result; - - static const char* function = "boost::math::logpdf(const chi_squared_distribution<%1%>&, %1%)"; - - if(false == detail::check_df(function, k, &error_result, Policy())) - { - return error_result; - } - - if((chi_square < 0) || !(boost::math::isfinite)(chi_square)) - { - return policies::raise_domain_error( - function, "Chi Square parameter was %1%, but must be > 0 !", chi_square, Policy()); - } - - if(chi_square == 0) - { - // Handle special cases: - if(k < 2) - { - return policies::raise_overflow_error(function, 0, Policy()); - } - else if(k == 2) - { - return -boost::math::constants::ln_two(); - } - else - { - return -std::numeric_limits::infinity(); - } - } - - return log(pdf(dist, chi_square)); -} // logpdf - template inline RealType cdf(const chi_squared_distribution& dist, const RealType& chi_square) { @@ -265,17 +224,6 @@ inline RealType mode(const chi_squared_distribution& dist) { RealType df = dist.degrees_of_freedom(); static const char* function = "boost::math::mode(const chi_squared_distribution<%1%>&)"; - // Most sources only define mode for df >= 2, - // but for 0 <= df <= 2, the pdf maximum actually occurs at random variate = 0; - // So one could extend the definition of mode thus: - //if(df < 0) - //{ - // return policies::raise_domain_error( - // function, - // "Chi-Squared distribution only has a mode for degrees of freedom >= 0, but got degrees of freedom = %1%.", - // df, Policy()); - //} - //return (df <= 2) ? 0 : df - 2; if(df < 2) return policies::raise_domain_error( @@ -285,25 +233,12 @@ inline RealType mode(const chi_squared_distribution& dist) return df - 2; } -//template -//inline RealType median(const chi_squared_distribution& dist) -//{ // Median is given by Quantile[dist, 1/2] -// RealType df = dist.degrees_of_freedom(); -// if(df <= 1) -// return tools::domain_error( -// BOOST_CURRENT_FUNCTION, -// "The Chi-Squared distribution only has a mode for degrees of freedom >= 2, but got degrees of freedom = %1%.", -// df); -// return df - RealType(2)/3; -//} -// Now implemented via quantile(half) in derived accessors. - template inline RealType skewness(const chi_squared_distribution& dist) { BOOST_MATH_STD_USING // For ADL RealType df = dist.degrees_of_freedom(); - return sqrt (8 / df); // == 2 * sqrt(2 / df); + return sqrt (8 / df); } template diff --git a/include/boost/math/distributions/exponential.hpp b/include/boost/math/distributions/exponential.hpp index 03295e94e9..5214575a64 100644 --- a/include/boost/math/distributions/exponential.hpp +++ b/include/boost/math/distributions/exponential.hpp @@ -60,10 +60,10 @@ template > class exponential_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - exponential_distribution(RealType l_lambda = 1) + explicit exponential_distribution(RealType l_lambda = 1) : m_lambda(l_lambda) { RealType err; @@ -76,7 +76,7 @@ class exponential_distribution RealType m_lambda; }; -typedef exponential_distribution exponential; +using exponential = exponential_distribution; #ifdef __cpp_deduction_guides template @@ -84,7 +84,7 @@ exponential_distribution(RealType)->exponential_distribution -inline const std::pair range(const exponential_distribution& /*dist*/) +inline std::pair range(const exponential_distribution& /*dist*/) { // Range of permissible values for random variable x. if (std::numeric_limits::has_infinity) { @@ -98,7 +98,7 @@ inline const std::pair range(const exponential_distribution< } template -inline const std::pair support(const exponential_distribution& /*dist*/) +inline std::pair support(const exponential_distribution& /*dist*/) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -135,7 +135,7 @@ inline RealType logpdf(const exponential_distribution& dist, c static const char* function = "boost::math::logpdf(const exponential_distribution<%1%>&, %1%)"; RealType lambda = dist.lambda(); - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); if(0 == detail::verify_lambda(function, lambda, &result, Policy())) return result; if(0 == detail::verify_exp_x(function, x, &result, Policy())) diff --git a/include/boost/math/distributions/extreme_value.hpp b/include/boost/math/distributions/extreme_value.hpp index 18b05071da..b2e8bea966 100644 --- a/include/boost/math/distributions/extreme_value.hpp +++ b/include/boost/math/distributions/extreme_value.hpp @@ -53,10 +53,10 @@ template > class extreme_value_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - extreme_value_distribution(RealType a = 0, RealType b = 1) + explicit extreme_value_distribution(RealType a = 0, RealType b = 1) : m_a(a), m_b(b) { RealType err; @@ -68,10 +68,11 @@ class extreme_value_distribution RealType scale()const { return m_b; } private: - RealType m_a, m_b; + RealType m_a; + RealType m_b; }; -typedef extreme_value_distribution extreme_value; +using extreme_value = extreme_value_distribution; #ifdef __cpp_deduction_guides template @@ -81,7 +82,7 @@ extreme_value_distribution(RealType,RealType)->extreme_value_distribution -inline const std::pair range(const extreme_value_distribution& /*dist*/) +inline std::pair range(const extreme_value_distribution& /*dist*/) { // Range of permissible values for random variable x. using boost::math::tools::max_value; return std::pair( @@ -90,7 +91,7 @@ inline const std::pair range(const extreme_value_distributio } template -inline const std::pair support(const extreme_value_distribution& /*dist*/) +inline std::pair support(const extreme_value_distribution& /*dist*/) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -131,7 +132,7 @@ inline RealType logpdf(const extreme_value_distribution& dist, RealType a = dist.location(); RealType b = dist.scale(); - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); if(0 == detail::verify_scale_b(function, b, &result, Policy())) return result; if(0 == detail::check_finite(function, a, &result, Policy())) @@ -145,7 +146,7 @@ inline RealType logpdf(const extreme_value_distribution& dist, result = log(1/b) + e - exp(e); // else.... result *must* be zero since exp(e) is infinite... return result; -} // pdf +} // logpdf template inline RealType cdf(const extreme_value_distribution& dist, const RealType& x) diff --git a/include/boost/math/distributions/gamma.hpp b/include/boost/math/distributions/gamma.hpp index f7838ed0da..83c2865958 100644 --- a/include/boost/math/distributions/gamma.hpp +++ b/include/boost/math/distributions/gamma.hpp @@ -72,10 +72,10 @@ template > class gamma_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - gamma_distribution(RealType l_shape, RealType l_scale = 1) + explicit gamma_distribution(RealType l_shape, RealType l_scale = 1) : m_shape(l_shape), m_scale(l_scale) { RealType result; @@ -109,14 +109,14 @@ gamma_distribution(RealType,RealType)->gamma_distribution -inline const std::pair range(const gamma_distribution& /* dist */) +inline std::pair range(const gamma_distribution& /* dist */) { // Range of permissible values for random variable x. using boost::math::tools::max_value; return std::pair(static_cast(0), max_value()); } template -inline const std::pair support(const gamma_distribution& /* dist */) +inline std::pair support(const gamma_distribution& /* dist */) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -159,7 +159,7 @@ inline RealType logpdf(const gamma_distribution& dist, const R RealType k = dist.shape(); RealType theta = dist.scale(); - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); if(false == detail::check_gamma(function, theta, k, &result, Policy())) return result; if(false == detail::check_gamma_x(function, x, &result, Policy())) diff --git a/include/boost/math/distributions/inverse_gamma.hpp b/include/boost/math/distributions/inverse_gamma.hpp index 91da8f34dd..eb348180de 100644 --- a/include/boost/math/distributions/inverse_gamma.hpp +++ b/include/boost/math/distributions/inverse_gamma.hpp @@ -89,10 +89,10 @@ template > class inverse_gamma_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - inverse_gamma_distribution(RealType l_shape = 1, RealType l_scale = 1) + explicit inverse_gamma_distribution(RealType l_shape = 1, RealType l_scale = 1) : m_shape(l_shape), m_scale(l_scale) { RealType result; @@ -118,10 +118,9 @@ class inverse_gamma_distribution RealType m_scale; // distribution scale }; -typedef inverse_gamma_distribution inverse_gamma; +using inverse_gamma = inverse_gamma_distribution; // typedef - but potential clash with name of inverse gamma *function*. -// but there is a typedef for gamma -// typedef boost::math::gamma_distribution gamma; +// but there is a typedef for the gamma distribution (gamma) #ifdef __cpp_deduction_guides template @@ -133,14 +132,14 @@ inverse_gamma_distribution(RealType,RealType)->inverse_gamma_distribution -inline const std::pair range(const inverse_gamma_distribution& /* dist */) +inline std::pair range(const inverse_gamma_distribution& /* dist */) { // Range of permissible values for random variable x. using boost::math::tools::max_value; return std::pair(static_cast(0), max_value()); } template -inline const std::pair support(const inverse_gamma_distribution& /* dist */) +inline std::pair support(const inverse_gamma_distribution& /* dist */) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -191,8 +190,7 @@ inline RealType pdf(const inverse_gamma_distribution& dist, co } result /= (x * x); } - // better than naive - // result = (pow(scale, shape) * pow(x, (-shape -1)) * exp(-scale/x) ) / tgamma(shape); + return result; } // pdf @@ -207,14 +205,14 @@ inline RealType logpdf(const inverse_gamma_distribution& dist, RealType shape = dist.shape(); RealType scale = dist.scale(); - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); if(false == detail::check_inverse_gamma(function, scale, shape, &result, Policy())) { // distribution parameters bad. return result; } if(x == 0) { // Treat random variate zero as a special case. - return 0; + return result; } else if(false == detail::check_inverse_gamma_x(function, x, &result, Policy())) { // x bad. @@ -222,7 +220,7 @@ inline RealType logpdf(const inverse_gamma_distribution& dist, } result = scale / x; if(result < tools::min_value()) - return 0; // random variable is infinite or so close as to make no difference. + return result; // random variable is infinite or so close as to make no difference. // x * x may under or overflow, likewise our result if (!(boost::math::isfinite)(x*x)) @@ -307,7 +305,6 @@ inline RealType cdf(const complemented2_type #include #include // for gamma function -// using boost::math::gamma_p; #include -//using std::tr1::tuple; -//using std::tr1::make_tuple; #include -//using boost::math::tools::newton_raphson_iterate; #include @@ -71,10 +67,10 @@ template > class inverse_gaussian_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - inverse_gaussian_distribution(RealType l_mean = 1, RealType l_scale = 1) + explicit inverse_gaussian_distribution(RealType l_mean = 1, RealType l_scale = 1) : m_mean(l_mean), m_scale(l_scale) { // Default is a 1,1 inverse_gaussian distribution. static const char* function = "boost::math::inverse_gaussian_distribution<%1%>::inverse_gaussian_distribution"; @@ -113,7 +109,7 @@ class inverse_gaussian_distribution RealType m_scale; // distribution standard deviation or scale, aka lambda. }; // class normal_distribution -typedef inverse_gaussian_distribution inverse_gaussian; +using inverse_gaussian = inverse_gaussian_distribution; #ifdef __cpp_deduction_guides template @@ -123,14 +119,14 @@ inverse_gaussian_distribution(RealType,RealType)->inverse_gaussian_distribution< #endif template -inline const std::pair range(const inverse_gaussian_distribution& /*dist*/) +inline std::pair range(const inverse_gaussian_distribution& /*dist*/) { // Range of permissible values for random variable x, zero to max. using boost::math::tools::max_value; return std::pair(static_cast(0.), max_value()); // - to + max value. } template -inline const std::pair support(const inverse_gaussian_distribution& /*dist*/) +inline std::pair support(const inverse_gaussian_distribution& /*dist*/) { // Range of supported values for random variable x, zero to max. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -181,7 +177,7 @@ inline RealType logpdf(const inverse_gaussian_distribution& di RealType scale = dist.scale(); RealType mean = dist.mean(); - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); static const char* function = "boost::math::logpdf(const inverse_gaussian_distribution<%1%>&, %1%)"; if(false == detail::check_scale(function, scale, &result, Policy())) { @@ -202,7 +198,7 @@ inline RealType logpdf(const inverse_gaussian_distribution& di if (x == 0) { - return 0; // Convenient, even if not defined mathematically. + return std::numeric_limits::quiet_NaN(); // Convenient, even if not defined mathematically. log(0) } const RealType two_pi = boost::math::constants::two_pi(); @@ -240,10 +236,7 @@ inline RealType cdf(const inverse_gaussian_distribution& dist, { return 0; // Convenient, even if not defined mathematically. } - // Problem with this formula for large scale > 1000 or small x, - //result = 0.5 * (erf(sqrt(scale / x) * ((x / mean) - 1) / constants::root_two(), Policy()) + 1) - // + exp(2 * scale / mean) / 2 - // * (1 - erf(sqrt(scale / x) * (x / mean + 1) / constants::root_two(), Policy())); + // Problem with this formula for large scale > 1000 or small x // so use normal distribution version: // Wikipedia CDF equation http://en.wikipedia.org/wiki/Inverse_Gaussian_distribution. @@ -307,22 +300,20 @@ namespace detail template inline RealType guess_ig(RealType p, RealType mu = 1, RealType lambda = 1) { // guess at random variate value x for inverse gaussian quantile. - BOOST_MATH_STD_USING - using boost::math::policies::policy; - // Error type. - using boost::math::policies::overflow_error; - // Action. - using boost::math::policies::ignore_error; + BOOST_MATH_STD_USING + using boost::math::policies::policy; + // Error type. + using boost::math::policies::overflow_error; + // Action. + using boost::math::policies::ignore_error; - typedef policy< - overflow_error // Ignore overflow (return infinity) - > no_overthrow_policy; + using no_overthrow_policy = policy>; RealType x; // result is guess at random variate value x. RealType phi = lambda / mu; if (phi > 2.) { // Big phi, so starting to look like normal Gaussian distribution. - // x=(qnorm(p,0,1,true,false) - 0.5 * sqrt(mu/lambda)) / sqrt(lambda/mu); + // // Whitmore, G.A. and Yalovsky, M. // A normalising logarithmic transformation for inverse Gaussian random variables, // Technometrics 20-2, 207-208 (1978), but using expression from @@ -337,14 +328,12 @@ namespace detail using boost::math::gamma_distribution; // Define the distribution, using gamma_nooverflow: - typedef gamma_distribution gamma_nooverflow; + using gamma_nooverflow = gamma_distribution; gamma_nooverflow g(static_cast(0.5), static_cast(1.)); - // gamma_nooverflow g(static_cast(0.5), static_cast(1.)); - // R qgamma(0.2, 0.5, 1) 0.0320923 + // R qgamma(0.2, 0.5, 1) = 0.0320923 RealType qg = quantile(complement(g, p)); - //RealType qg1 = qgamma(1.- p, 0.5, 1.0, true, false); x = lambda / (qg * 2); // if (x > mu/2) // x > mu /2? @@ -387,7 +376,7 @@ inline RealType quantile(const inverse_gaussian_distribution& { // overflow result = policies::raise_overflow_error(function, "probability parameter is 1, but must be < 1!", Policy()); - return result; // std::numeric_limits::infinity(); + return result; // infinity; } RealType guess = detail::guess_ig(p, dist.mean(), dist.scale()); @@ -415,21 +404,7 @@ inline RealType cdf(const complemented2_type::has_infinity && x == std::numeric_limits::infinity()) - //{ // cdf complement +infinity is zero. - // return 0; - //} - //if(std::numeric_limits::has_infinity && x == -std::numeric_limits::infinity()) - //{ // cdf complement -infinity is unity. - // return 1; - //} + RealType result = 0; if(false == detail::check_scale(function, scale, &result, Policy())) return result; diff --git a/include/boost/math/distributions/laplace.hpp b/include/boost/math/distributions/laplace.hpp index 081512240e..cc922a879a 100644 --- a/include/boost/math/distributions/laplace.hpp +++ b/include/boost/math/distributions/laplace.hpp @@ -36,13 +36,13 @@ class laplace_distribution // ---------------------------------- // public Types // ---------------------------------- - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; // ---------------------------------- // Constructor(s) // ---------------------------------- - laplace_distribution(RealType l_location = 0, RealType l_scale = 1) + explicit laplace_distribution(RealType l_location = 0, RealType l_scale = 1) : m_location(l_location), m_scale(l_scale) { RealType result; @@ -78,7 +78,7 @@ class laplace_distribution // // Convenient type synonym for double. -typedef laplace_distribution laplace; +using laplace = laplace_distribution; #ifdef __cpp_deduction_guides template @@ -90,9 +90,9 @@ laplace_distribution(RealType,RealType)->laplace_distribution -inline const std::pair range(const laplace_distribution&) +inline std::pair range(const laplace_distribution&) { - if (std::numeric_limits::has_infinity) + if (std::numeric_limits::has_infinity) { // Can use infinity. return std::pair(-std::numeric_limits::infinity(), std::numeric_limits::infinity()); // - to + infinity. } @@ -105,7 +105,7 @@ inline const std::pair range(const laplace_distribution -inline const std::pair support(const laplace_distribution&) +inline std::pair support(const laplace_distribution&) { if (std::numeric_limits::has_infinity) { // Can Use infinity. @@ -156,19 +156,23 @@ inline RealType logpdf(const laplace_distribution& dist, const BOOST_MATH_STD_USING // for ADL of std functions // Checking function argument - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); const char* function = "boost::math::logpdf(const laplace_distribution<%1%>&, %1%))"; // Check scale and location. - if (false == dist.check_parameters(function, &result)) - return result; + if (false == dist.check_parameters(function, &result)) + { + return result; + } // Special pdf values. if((boost::math::isinf)(x)) { - return 0; // pdf + and - infinity is zero. + return result; // pdf + and - infinity is zero so logpdf is -INF + } + if (false == detail::check_x(function, x, &result, Policy())) + { + return result; } - if (false == detail::check_x(function, x, &result, Policy())) - return result; const RealType mu = dist.scale(); const RealType b = dist.location(); @@ -239,14 +243,14 @@ inline RealType quantile(const laplace_distribution& dist, con { result = policies::raise_overflow_error(function, "probability parameter is 0, but must be > 0!", Policy()); - return -result; // -std::numeric_limits::infinity(); + return -result; // -inf } if(p == 1) { result = policies::raise_overflow_error(function, "probability parameter is 1, but must be < 1!", Policy()); - return result; // std::numeric_limits::infinity(); + return result; // inf } // Calculate Quantile RealType scale( dist.scale() ); @@ -276,8 +280,6 @@ inline RealType cdf(const complemented2_type > class normal_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - normal_distribution(RealType l_mean = 0, RealType sd = 1) + explicit normal_distribution(RealType l_mean = 0, RealType sd = 1) : m_mean(l_mean), m_sd(sd) { // Default is a 'standard' normal distribution N01. static const char* function = "boost::math::normal_distribution<%1%>::normal_distribution"; @@ -70,7 +70,7 @@ class normal_distribution RealType m_sd; // distribution standard deviation or scale. }; // class normal_distribution -typedef normal_distribution normal; +using normal = normal_distribution; // // Deduction guides, note we don't check the @@ -92,7 +92,7 @@ normal_distribution(RealType)->normal_distribution -inline const std::pair range(const normal_distribution& /*dist*/) +inline std::pair range(const normal_distribution& /*dist*/) { // Range of permissible values for random variable x. if (std::numeric_limits::has_infinity) { @@ -106,7 +106,7 @@ inline const std::pair range(const normal_distribution -inline const std::pair support(const normal_distribution& /*dist*/) +inline std::pair support(const normal_distribution& /*dist*/) { // This is range values for random variable x where cdf rises from 0 to 1, and outside it, the pdf is zero. if (std::numeric_limits::has_infinity) { @@ -146,11 +146,6 @@ inline RealType pdf(const normal_distribution& dist, const Rea { return 0; // pdf + and - infinity is zero. } - // Below produces MSVC 4127 warnings, so the above used instead. - //if(std::numeric_limits::has_infinity && abs(x) == std::numeric_limits::infinity()) - //{ // pdf + and - infinity is zero. - // return 0; - //} if(false == detail::check_x(function, x, &result, Policy())) { return result; @@ -171,12 +166,12 @@ inline RealType logpdf(const normal_distribution& dist, const { BOOST_MATH_STD_USING // for ADL of std functions - RealType sd = dist.standard_deviation(); - RealType mean = dist.mean(); + const RealType sd = dist.standard_deviation(); + const RealType mean = dist.mean(); static const char* function = "boost::math::logpdf(const normal_distribution<%1%>&, %1%)"; - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); if(false == detail::check_scale(function, sd, &result, Policy())) { return result; @@ -187,7 +182,7 @@ inline RealType logpdf(const normal_distribution& dist, const } if((boost::math::isinf)(x)) { - return 0; // pdf + and - infinity is zero. + return result; // pdf + and - infinity is zero so logpdf is -inf } if(false == detail::check_x(function, x, &result, Policy())) { @@ -224,15 +219,6 @@ inline RealType cdf(const normal_distribution& dist, const Rea if(x < 0) return 0; // -infinity return 1; // + infinity } - // These produce MSVC 4127 warnings, so the above used instead. - //if(std::numeric_limits::has_infinity && x == std::numeric_limits::infinity()) - //{ // cdf +infinity is unity. - // return 1; - //} - //if(std::numeric_limits::has_infinity && x == -std::numeric_limits::infinity()) - //{ // cdf -infinity is zero. - // return 0; - //} if(false == detail::check_x(function, x, &result, Policy())) { return result; @@ -286,15 +272,6 @@ inline RealType cdf(const complemented2_type::has_infinity && x == std::numeric_limits::infinity()) - //{ // cdf complement +infinity is zero. - // return 0; - //} - //if(std::numeric_limits::has_infinity && x == -std::numeric_limits::infinity()) - //{ // cdf complement -infinity is unity. - // return 1; - //} if(false == detail::check_x(function, x, &result, Policy())) return result; diff --git a/include/boost/math/distributions/poisson.hpp b/include/boost/math/distributions/poisson.hpp index 921b9f027c..dd5e8062fd 100644 --- a/include/boost/math/distributions/poisson.hpp +++ b/include/boost/math/distributions/poisson.hpp @@ -145,10 +145,10 @@ namespace boost class poisson_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - poisson_distribution(RealType l_mean = 1) : m_l(l_mean) // mean (lambda). + explicit poisson_distribution(RealType l_mean = 1) : m_l(l_mean) // mean (lambda). { // Expected mean number of events that occur during the given interval. RealType r; poisson_detail::check_dist( @@ -166,7 +166,7 @@ namespace boost RealType m_l; // mean number of occurrences. }; // template class poisson_distribution - typedef poisson_distribution poisson; // Reserved name of type double. + using poisson = poisson_distribution; // Reserved name of type double. #ifdef __cpp_deduction_guides template @@ -176,14 +176,14 @@ namespace boost // Non-member functions to give properties of the distribution. template - inline const std::pair range(const poisson_distribution& /* dist */) + inline std::pair range(const poisson_distribution& /* dist */) { // Range of permissible values for random variable k. using boost::math::tools::max_value; return std::pair(static_cast(0), max_value()); // Max integer? } template - inline const std::pair support(const poisson_distribution& /* dist */) + inline std::pair support(const poisson_distribution& /* dist */) { // Range of supported values for random variable k. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -203,15 +203,7 @@ namespace boost return floor(dist.mean()); } - //template - //inline RealType median(const poisson_distribution& dist) - //{ // median = approximately lambda + 1/3 - 0.2/lambda - // RealType l = dist.mean(); - // return dist.mean() + static_cast(0.3333333333333333333333333333333333333333333333) - // - static_cast(0.2) / l; - //} // BUT this formula appears to be out-by-one compared to quantile(half) - // Query posted on Wikipedia. - // Now implemented via quantile(half) in derived accessors. + // Median now implemented via quantile(half) in derived accessors. template inline RealType variance(const poisson_distribution& dist) @@ -219,7 +211,6 @@ namespace boost return dist.mean(); } - // RealType standard_deviation(const poisson_distribution& dist) // standard_deviation provided by derived accessors. template @@ -292,7 +283,7 @@ namespace boost RealType mean = dist.mean(); // Error check: - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); if(false == poisson_detail::check_dist_and_k( "boost::math::pdf(const poisson_distribution<%1%>&, %1%)", mean, @@ -357,8 +348,8 @@ namespace boost return 0; } if (k == 0) - { // return pdf(dist, static_cast(0)); - // but mean (and k) have already been checked, + { + // mean (and k) have already been checked, // so this avoids unnecessary repeated checks. return exp(-mean); } @@ -451,9 +442,10 @@ namespace boost { return policies::raise_overflow_error(function, 0, Policy()); } - typedef typename Policy::discrete_quantile_type discrete_type; + using discrete_type = typename Policy::discrete_quantile_type; std::uintmax_t max_iter = policies::get_max_root_iterations(); - RealType guess, factor = 8; + RealType guess; + RealType factor = 8; RealType z = dist.mean(); if(z < 1) guess = z; @@ -521,9 +513,10 @@ namespace boost { return 0; // Exact result regardless of discrete-quantile Policy } - typedef typename Policy::discrete_quantile_type discrete_type; + using discrete_type = typename Policy::discrete_quantile_type; std::uintmax_t max_iter = policies::get_max_root_iterations(); - RealType guess, factor = 8; + RealType guess; + RealType factor = 8; RealType z = dist.mean(); if(z < 1) guess = z; diff --git a/include/boost/math/distributions/rayleigh.hpp b/include/boost/math/distributions/rayleigh.hpp index 83f5bc8709..cbbf39876d 100644 --- a/include/boost/math/distributions/rayleigh.hpp +++ b/include/boost/math/distributions/rayleigh.hpp @@ -56,10 +56,10 @@ template > class rayleigh_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - rayleigh_distribution(RealType l_sigma = 1) + explicit rayleigh_distribution(RealType l_sigma = 1) : m_sigma(l_sigma) { RealType err; @@ -75,7 +75,7 @@ class rayleigh_distribution RealType m_sigma; }; // class rayleigh_distribution -typedef rayleigh_distribution rayleigh; +using rayleigh = rayleigh_distribution; #ifdef __cpp_deduction_guides template @@ -83,14 +83,14 @@ rayleigh_distribution(RealType)->rayleigh_distribution -inline const std::pair range(const rayleigh_distribution& /*dist*/) +inline std::pair range(const rayleigh_distribution& /*dist*/) { // Range of permissible values for random variable x. using boost::math::tools::max_value; return std::pair(static_cast(0), std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : max_value()); } template -inline const std::pair support(const rayleigh_distribution& /*dist*/) +inline std::pair support(const rayleigh_distribution& /*dist*/) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -128,7 +128,7 @@ inline RealType logpdf(const rayleigh_distribution& dist, cons BOOST_MATH_STD_USING // for ADL of std function exp. const RealType sigma = dist.sigma(); - RealType result = 0; + RealType result = -std::numeric_limits::infinity(); static const char* function = "boost::math::logpdf(const rayleigh_distribution<%1%>&, %1%)"; if(false == detail::verify_sigma(function, sigma, &result, Policy())) @@ -141,7 +141,7 @@ inline RealType logpdf(const rayleigh_distribution& dist, cons } if((boost::math::isinf)(x)) { - return 0; + return result; } result = -(x*x)/(2*sigma*sigma) - 2*log(sigma) + log(x); @@ -291,31 +291,26 @@ inline RealType median(const rayleigh_distribution& dist) template inline RealType skewness(const rayleigh_distribution& /*dist*/) { - // using namespace boost::math::constants; return static_cast(0.63111065781893713819189935154422777984404221106391L); // Computed using NTL at 150 bit, about 50 decimal digits. - // return 2 * root_pi() * pi_minus_three() / pow23_four_minus_pi(); + // 2 * sqrt(pi) * (pi-3) / pow(4, 2/3) - pi } template inline RealType kurtosis(const rayleigh_distribution& /*dist*/) { - // using namespace boost::math::constants; return static_cast(3.2450893006876380628486604106197544154170667057995L); // Computed using NTL at 150 bit, about 50 decimal digits. - // return 3 - (6 * pi() * pi() - 24 * pi() + 16) / - // (four_minus_pi() * four_minus_pi()); + // 3 - (6*pi*pi - 24*pi + 16) / pow(4-pi, 2) } template inline RealType kurtosis_excess(const rayleigh_distribution& /*dist*/) { - //using namespace boost::math::constants; - // Computed using NTL at 150 bit, about 50 decimal digits. return static_cast(0.2450893006876380628486604106197544154170667057995L); - // return -(6 * pi() * pi() - 24 * pi() + 16) / - // (four_minus_pi() * four_minus_pi()); -} // kurtosis + // Computed using NTL at 150 bit, about 50 decimal digits. + // -(6*pi*pi - 24*pi + 16) / pow(4-pi,2) +} // kurtosis_excess template inline RealType entropy(const rayleigh_distribution& dist) diff --git a/include/boost/math/distributions/weibull.hpp b/include/boost/math/distributions/weibull.hpp index 572c96716c..724cce6ed8 100644 --- a/include/boost/math/distributions/weibull.hpp +++ b/include/boost/math/distributions/weibull.hpp @@ -70,10 +70,10 @@ template > class weibull_distribution { public: - typedef RealType value_type; - typedef Policy policy_type; + using value_type = RealType; + using policy_type = Policy; - weibull_distribution(RealType l_shape, RealType l_scale = 1) + explicit weibull_distribution(RealType l_shape, RealType l_scale = 1) : m_shape(l_shape), m_scale(l_scale) { RealType result; @@ -97,7 +97,7 @@ class weibull_distribution RealType m_scale; // distribution scale }; -typedef weibull_distribution weibull; +using weibull = weibull_distribution; #ifdef __cpp_deduction_guides template @@ -107,14 +107,14 @@ weibull_distribution(RealType,RealType)->weibull_distribution -inline const std::pair range(const weibull_distribution& /*dist*/) +inline std::pair range(const weibull_distribution& /*dist*/) { // Range of permissible values for random variable x. using boost::math::tools::max_value; return std::pair(static_cast(0), max_value()); } template -inline const std::pair support(const weibull_distribution& /*dist*/) +inline std::pair support(const weibull_distribution& /*dist*/) { // Range of supported values for random variable x. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; @@ -186,7 +186,7 @@ inline RealType logpdf(const weibull_distribution& dist, const return policies::raise_overflow_error(function, 0, Policy()); } - result = log(shape * pow(scale, -shape) * pow(x, shape - 1)) - pow(x / scale, shape); + result = log(shape) - shape * log(scale) + log(x) * (shape - 1) - pow(x / scale, shape); return result; } @@ -376,12 +376,11 @@ inline RealType skewness(const weibull_distribution& dist) { return result; } - RealType g1, g2, g3, d; - g1 = boost::math::tgamma(1 + 1 / shape, Policy()); - g2 = boost::math::tgamma(1 + 2 / shape, Policy()); - g3 = boost::math::tgamma(1 + 3 / shape, Policy()); - d = pow(g2 - g1 * g1, RealType(1.5)); + RealType g1 = boost::math::tgamma(1 + 1 / shape, Policy()); + RealType g2 = boost::math::tgamma(1 + 2 / shape, Policy()); + RealType g3 = boost::math::tgamma(1 + 3 / shape, Policy()); + RealType d = pow(g2 - g1 * g1, RealType(1.5)); result = (2 * g1 * g1 * g1 - 3 * g1 * g2 + g3) / d; return result; @@ -401,15 +400,13 @@ inline RealType kurtosis_excess(const weibull_distribution& di if(false == detail::check_weibull(function, scale, shape, &result, Policy())) return result; - RealType g1, g2, g3, g4, d, g1_2, g1_4; - - g1 = boost::math::tgamma(1 + 1 / shape, Policy()); - g2 = boost::math::tgamma(1 + 2 / shape, Policy()); - g3 = boost::math::tgamma(1 + 3 / shape, Policy()); - g4 = boost::math::tgamma(1 + 4 / shape, Policy()); - g1_2 = g1 * g1; - g1_4 = g1_2 * g1_2; - d = g2 - g1_2; + RealType g1 = boost::math::tgamma(1 + 1 / shape, Policy()); + RealType g2 = boost::math::tgamma(1 + 2 / shape, Policy()); + RealType g3 = boost::math::tgamma(1 + 3 / shape, Policy()); + RealType g4 = boost::math::tgamma(1 + 4 / shape, Policy()); + RealType g1_2 = g1 * g1; + RealType g1_4 = g1_2 * g1_2; + RealType d = g2 - g1_2; d *= d; result = -6 * g1_4 + 12 * g1_2 * g2 - 3 * g2 * g2 - 4 * g1 * g3 + g4; From 73708be8742751320fabe53f3415a8530c60ea64 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Sat, 16 Apr 2022 17:34:25 -0700 Subject: [PATCH 26/60] Add logpdf to compile tests --- .gitignore | 1 + test/compile_test/test_compile_result.hpp | 51 ++++++++++++----------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 2b74895e20..5ff21efb64 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ DartConfiguration.tcl cmake-build-debug/* .cmake/* build.ninja +.ninja* diff --git a/test/compile_test/test_compile_result.hpp b/test/compile_test/test_compile_result.hpp index a86d390fac..0afdced913 100644 --- a/test/compile_test/test_compile_result.hpp +++ b/test/compile_test/test_compile_result.hpp @@ -9,14 +9,12 @@ // detect missing includes). // -static const float f = 0; -static const double d = 0; -static const long double l = 0; -static const unsigned u = 0; -static const int i = 0; - -//template -//inline void check_result_imp(T, T){} +static constexpr float f = 0; +static constexpr double d = 0; +static constexpr long double l = 0; +static constexpr unsigned u = 0; +static constexpr int i = 0; +static constexpr unsigned long li = 1; inline void check_result_imp(float, float){} inline void check_result_imp(double, double){} @@ -38,12 +36,12 @@ inline void check_result_imp(bool, bool){} template struct local_is_same { - enum{ value = false }; + static constexpr bool value = false; }; template struct local_is_same { - enum{ value = true }; + static constexpr bool value = true; }; template @@ -54,7 +52,7 @@ inline void check_result_imp(T1, T2) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #endif - typedef BOOST_MATH_ASSERT_UNUSED_ATTRIBUTE int static_assertion[local_is_same::value ? 1 : 0]; + using static_assertion = int[local_is_same::value ? 1 : 0]; #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))) #pragma GCC diagnostic pop #endif @@ -68,23 +66,12 @@ inline void check_result(T2) return check_result_imp(a, b); } -union max_align_type -{ - char c; - short s; - int i; - long l; - double d; - long double ld; - long long ll; -}; - template struct DistributionConcept { static void constraints() { - typedef typename Distribution::value_type value_type; + using value_type = typename Distribution::value_type; const Distribution& dist = DistributionConcept::get_object(); @@ -93,6 +80,7 @@ struct DistributionConcept check_result(cdf(dist, x)); check_result(cdf(complement(dist, x))); check_result(pdf(dist, x)); + check_result(logpdf(dist, x)); check_result(quantile(dist, x)); check_result(quantile(complement(dist, x))); check_result(mean(dist)); @@ -116,6 +104,7 @@ struct DistributionConcept check_result(cdf(dist, f)); check_result(cdf(complement(dist, f))); check_result(pdf(dist, f)); + check_result(logpdf(dist, f)); check_result(quantile(dist, f)); check_result(quantile(complement(dist, f))); check_result(hazard(dist, f)); @@ -123,6 +112,7 @@ struct DistributionConcept check_result(cdf(dist, d)); check_result(cdf(complement(dist, d))); check_result(pdf(dist, d)); + check_result(logpdf(dist, d)); check_result(quantile(dist, d)); check_result(quantile(complement(dist, d))); check_result(hazard(dist, d)); @@ -130,6 +120,7 @@ struct DistributionConcept check_result(cdf(dist, l)); check_result(cdf(complement(dist, l))); check_result(pdf(dist, l)); + check_result(logpdf(dist, l)); check_result(quantile(dist, l)); check_result(quantile(complement(dist, l))); check_result(hazard(dist, l)); @@ -137,20 +128,32 @@ struct DistributionConcept check_result(cdf(dist, i)); check_result(cdf(complement(dist, i))); check_result(pdf(dist, i)); + check_result(logpdf(dist, i)); check_result(quantile(dist, i)); check_result(quantile(complement(dist, i))); check_result(hazard(dist, i)); check_result(chf(dist, i)); - unsigned long li = 1; check_result(cdf(dist, li)); check_result(cdf(complement(dist, li))); check_result(pdf(dist, li)); + check_result(logpdf(dist, li)); check_result(quantile(dist, li)); check_result(quantile(complement(dist, li))); check_result(hazard(dist, li)); check_result(chf(dist, li)); } private: + union max_align_type + { + char c; + short s; + int i; + long l; + double d; + long double ld; + long long ll; + }; + static void* storage() { static max_align_type storage[sizeof(Distribution)]; From 4dcddfb8017818fa393a152cfe9d8aa53296e6fa Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 18 Apr 2022 09:22:31 -0700 Subject: [PATCH 27/60] Replace uses of log(tgamma(x)) with lgamma(x) --- include/boost/math/distributions/gamma.hpp | 14 +++----------- include/boost/math/distributions/inverse_gamma.hpp | 4 ++-- include/boost/math/distributions/poisson.hpp | 4 ++-- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/include/boost/math/distributions/gamma.hpp b/include/boost/math/distributions/gamma.hpp index 83c2865958..28b7c55b0b 100644 --- a/include/boost/math/distributions/gamma.hpp +++ b/include/boost/math/distributions/gamma.hpp @@ -152,7 +152,7 @@ template inline RealType logpdf(const gamma_distribution& dist, const RealType& x) { BOOST_MATH_STD_USING // for ADL of std functions - using boost::math::tgamma; + using boost::math::lgamma; static const char* function = "boost::math::logpdf(const gamma_distribution<%1%>&, %1%)"; @@ -169,16 +169,8 @@ inline RealType logpdf(const gamma_distribution& dist, const R { return std::numeric_limits::quiet_NaN(); } - - // The following calculation does not always work with float so take the naive road out - BOOST_IF_CONSTEXPR(std::is_same::value) - { - result = log(pdf(dist, x)); - } - else - { - result = -k*log(theta) + (k-1)*log(x) - log(tgamma(k)) - (x/theta); - } + + result = -k*log(theta) + (k-1)*log(x) - lgamma(k) - (x/theta); return result; } // logpdf diff --git a/include/boost/math/distributions/inverse_gamma.hpp b/include/boost/math/distributions/inverse_gamma.hpp index eb348180de..8c9e4763d5 100644 --- a/include/boost/math/distributions/inverse_gamma.hpp +++ b/include/boost/math/distributions/inverse_gamma.hpp @@ -198,7 +198,7 @@ template inline RealType logpdf(const inverse_gamma_distribution& dist, const RealType& x) { BOOST_MATH_STD_USING // for ADL of std functions - using boost::math::tgamma; + using boost::math::lgamma; static const char* function = "boost::math::logpdf(const inverse_gamma_distribution<%1%>&, %1%)"; @@ -228,7 +228,7 @@ inline RealType logpdf(const inverse_gamma_distribution& dist, return policies::raise_overflow_error(function, "PDF is infinite.", Policy()); } - return shape * log(scale) + (-shape-1)*log(x) - log(tgamma(shape)) - (scale/x); + return shape * log(scale) + (-shape-1)*log(x) - lgamma(shape) - (scale/x); } // pdf template diff --git a/include/boost/math/distributions/poisson.hpp b/include/boost/math/distributions/poisson.hpp index dd5e8062fd..5507360e82 100644 --- a/include/boost/math/distributions/poisson.hpp +++ b/include/boost/math/distributions/poisson.hpp @@ -279,7 +279,7 @@ namespace boost BOOST_FPU_EXCEPTION_GUARD BOOST_MATH_STD_USING // for ADL of std functions. - using boost::math::tgamma; + using boost::math::lgamma; RealType mean = dist.mean(); // Error check: @@ -302,7 +302,7 @@ namespace boost // Special case where k and lambda are both positive if(k > 0 && mean > 0) { - return -log(tgamma(k+1)) + k*log(mean) - mean; + return -lgamma(k+1) + k*log(mean) - mean; } result = log(pdf(dist, k)); From 51dc64e867cab4078790e1fdbddb8b8fb4d55118 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 18 Apr 2022 09:23:20 -0700 Subject: [PATCH 28/60] Fix for issue 773 (#782) --- include/boost/math/tools/color_maps.hpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/include/boost/math/tools/color_maps.hpp b/include/boost/math/tools/color_maps.hpp index 073f65d1ef..e3cc8b9b09 100644 --- a/include/boost/math/tools/color_maps.hpp +++ b/include/boost/math/tools/color_maps.hpp @@ -10,6 +10,7 @@ #include // for table data #include // for std::floor #include // fixed width integer types +#include #if __has_include("lodepng.h") @@ -1863,12 +1864,21 @@ static constexpr std::array, 256> viridis_data_ = { template inline std::array color_map_(Real scalar, std::array, 256> const &table) { - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point_v, "Color tables are only implemented in floating point " "arithmetic. If you require bytes please submit an issue or " "pull request"); - scalar = std::clamp(scalar, static_cast(0), static_cast(1)); + using boost::math::isnan; + + if ((isnan)(scalar)) + { + scalar = static_cast(0); + } + else + { + scalar = std::clamp(scalar, static_cast(0), static_cast(1)); + } if (scalar == static_cast(1)) { return table.back(); @@ -1877,7 +1887,7 @@ color_map_(Real scalar, std::array, 256> const &table) { Real s = (table.size() - 1) * scalar; Real ii = std::floor(s); Real t = s - ii; - std::size_t i = static_cast(ii); + auto i = static_cast(ii); auto const &rgb0 = table[i]; auto const &rgb1 = table[i + 1]; return {(1 - t) * rgb0[0] + t * rgb1[0], (1 - t) * rgb0[1] + t * rgb1[1], @@ -1917,7 +1927,7 @@ std::array extended_kindlmann(Real x) { template std::array to_8bit_rgba(const std::array &v) { using std::sqrt; - std::array pixel; + std::array pixel {}; for (auto i = 0; i < 3; ++i) { // Apply gamma correction here: Real u = sqrt(v[i]); From 3687f1e88df2d0e3b062d410accdc854ac712308 Mon Sep 17 00:00:00 2001 From: Christopher Kormanyos Date: Tue, 10 May 2022 08:28:22 +0200 Subject: [PATCH 29/60] Redeclaration of max_factorial value --- .../boost/math/special_functions/detail/unchecked_factorial.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/math/special_functions/detail/unchecked_factorial.hpp b/include/boost/math/special_functions/detail/unchecked_factorial.hpp index 3bdc45fe5f..5fa9f0b6b4 100644 --- a/include/boost/math/special_functions/detail/unchecked_factorial.hpp +++ b/include/boost/math/special_functions/detail/unchecked_factorial.hpp @@ -1009,7 +1009,7 @@ struct max_factorial #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION template -const unsigned max_factorial::value; +constexpr unsigned max_factorial::value; #endif } // namespace math From 926e34c55ce12923e78d13762e5194bab3f7c47f Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 12 May 2022 19:42:24 +0100 Subject: [PATCH 30/60] Set BOOST_NO_EXCEPTIONS and BOOST_NO_RTTI for GCC and math in standalone mode. Disable everything not needed in error_handling.hpp when no exceptions are available. --- .../boost/math/policies/error_handling.hpp | 51 +++++++++++++++++-- include/boost/math/tools/config.hpp | 19 +++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/include/boost/math/policies/error_handling.hpp b/include/boost/math/policies/error_handling.hpp index 4ee6877e27..3af1f976b5 100644 --- a/include/boost/math/policies/error_handling.hpp +++ b/include/boost/math/policies/error_handling.hpp @@ -8,19 +8,23 @@ #ifndef BOOST_MATH_POLICY_ERROR_HANDLING_HPP #define BOOST_MATH_POLICY_ERROR_HANDLING_HPP +#include #include #include #include +#ifndef BOOST_NO_RTTI #include +#endif #include #include #include #include -#include -#include #include #include +#ifndef BOOST_NO_EXCEPTIONS +#include #include +#endif #ifdef _MSC_VER # pragma warning(push) // Quiet warnings in boost/format.hpp @@ -36,6 +40,8 @@ namespace boost{ namespace math{ +#ifndef BOOST_NO_EXCEPTIONS + class evaluation_error : public std::runtime_error { public: @@ -48,6 +54,8 @@ class rounding_error : public std::runtime_error rounding_error(const std::string& s) : std::runtime_error(s){} }; +#endif + namespace policies{ // // Forward declarations of user error handlers, @@ -120,6 +128,7 @@ inline const char* name_of() } #endif +#ifndef BOOST_NO_EXCEPTIONS template void raise_error(const char* pfunction, const char* message) { @@ -169,6 +178,7 @@ void raise_error(const char* pfunction, const char* pmessage, const T& val) E e(msg); BOOST_MATH_THROW_EXCEPTION(e) } +#endif template inline T raise_domain_error( @@ -177,9 +187,13 @@ inline T raise_domain_error( const T& val, const ::boost::math::policies::domain_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message, val); // we never get here: return std::numeric_limits::quiet_NaN(); +#endif } template @@ -224,7 +238,11 @@ inline T raise_pole_error( const T& val, const ::boost::math::policies::pole_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else return boost::math::policies::detail::raise_domain_error(function, message, val, ::boost::math::policies::domain_error< ::boost::math::policies::throw_on_error>()); +#endif } template @@ -257,16 +275,19 @@ inline T raise_pole_error( return user_pole_error(function, message, val); } - template inline T raise_overflow_error( const char* function, const char* message, const ::boost::math::policies::overflow_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message ? message : "numeric overflow"); // We should never get here: return std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : boost::math::tools::max_value(); +#endif } template @@ -276,9 +297,13 @@ inline T raise_overflow_error( const T& val, const ::boost::math::policies::overflow_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message ? message : "numeric overflow", val); // We should never get here: return std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : boost::math::tools::max_value(); +#endif } template @@ -358,9 +383,13 @@ inline T raise_underflow_error( const char* message, const ::boost::math::policies::underflow_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message ? message : "numeric underflow"); // We should never get here: return 0; +#endif } template @@ -402,9 +431,13 @@ inline T raise_denorm_error( const T& /* val */, const ::boost::math::policies::denorm_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message ? message : "denormalised result"); // we never get here: return T(0); +#endif } template @@ -449,9 +482,13 @@ inline T raise_evaluation_error( const T& val, const ::boost::math::policies::evaluation_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message, val); // we never get here: return T(0); +#endif } template @@ -497,9 +534,13 @@ inline TargetType raise_rounding_error( const TargetType&, const ::boost::math::policies::rounding_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message, val); // we never get here: return TargetType(0); +#endif } template @@ -564,9 +605,13 @@ inline T raise_indeterminate_result_error( const R& , const ::boost::math::policies::indeterminate_result_error< ::boost::math::policies::throw_on_error>&) { +#ifdef BOOST_NO_EXCEPTIONS + static_assert(sizeof(T) == 0, "Error handler called with throw_on_error and BOOST_NO_EXCEPTIONS set."); +#else raise_error(function, message, val); // we never get here: return std::numeric_limits::quiet_NaN(); +#endif } template diff --git a/include/boost/math/tools/config.hpp b/include/boost/math/tools/config.hpp index 2a054fc261..b766ec4ff8 100644 --- a/include/boost/math/tools/config.hpp +++ b/include/boost/math/tools/config.hpp @@ -77,6 +77,25 @@ # define BOOST_NO_CXX11_THREAD_LOCAL #endif // BOOST_DISABLE_THREADS +#ifdef __GNUC__ +# if !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +# endif + // + // Make sure we have some std lib headers included so we can detect __GXX_RTTI: + // +# include // for min and max +# include +# ifndef __GXX_RTTI +# ifndef BOOST_NO_TYPEID +# define BOOST_NO_TYPEID +# endif +# ifndef BOOST_NO_RTTI +# define BOOST_NO_RTTI +# endif +# endif +#endif + #endif // BOOST_MATH_STANDALONE #include // for min and max From a6db2d19b7e96f802af751b039090d184343b3b5 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 13 May 2022 19:03:42 +0100 Subject: [PATCH 31/60] Change bernoulli error handling to always go through the policies. --- .../detail/bernoulli_details.hpp | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/include/boost/math/special_functions/detail/bernoulli_details.hpp b/include/boost/math/special_functions/detail/bernoulli_details.hpp index 91f1747c01..58015fec14 100644 --- a/include/boost/math/special_functions/detail/bernoulli_details.hpp +++ b/include/boost/math/special_functions/detail/bernoulli_details.hpp @@ -184,17 +184,22 @@ struct fixed_vector : private std::allocator const T& operator[](unsigned n)const { BOOST_MATH_ASSERT(n < m_used); return m_data[n]; } unsigned size()const { return m_used; } unsigned size() { return m_used; } - void resize(unsigned n, const T& val) + bool resize(unsigned n, const T& val) { if(n > m_capacity) { +#ifndef BOOST_NO_EXCEPTIONS BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Exhausted storage for Bernoulli numbers.")); +#else + return false; +#endif } for(unsigned i = m_used; i < n; ++i) new (m_data + i) T(val); m_used = n; + return true; } - void resize(unsigned n) { resize(n, T()); } + bool resize(unsigned n) { return resize(n, T()); } T* begin() { return m_data; } T* end() { return m_data + m_used; } T* begin()const { return m_data; } @@ -217,10 +222,14 @@ class bernoulli_numbers_cache typedef fixed_vector container_type; - void tangent(std::size_t m) + bool tangent(std::size_t m) { static const std::size_t min_overflow_index = b2n_overflow_limit() - 1; - tn.resize(static_cast(m), T(0U)); + + if (!tn.resize(static_cast(m), T(0U))) + { + return false; + } BOOST_MATH_INSTRUMENT_VARIABLE(min_overflow_index); @@ -268,17 +277,20 @@ class bernoulli_numbers_cache BOOST_MATH_INSTRUMENT_VARIABLE(i); BOOST_MATH_INSTRUMENT_VARIABLE(tn[static_cast(i)]); } + return true; } - void tangent_numbers_series(const std::size_t m) + bool tangent_numbers_series(const std::size_t m) { BOOST_MATH_STD_USING static const std::size_t min_overflow_index = b2n_overflow_limit() - 1; typename container_type::size_type old_size = bn.size(); - tangent(m); - bn.resize(static_cast(m)); + if (!tangent(m)) + return false; + if (!bn.resize(static_cast(m))) + return false; if(!old_size) { @@ -321,6 +333,7 @@ class bernoulli_numbers_cache bn[static_cast(i)] = ((!b_neg) ? b : T(-b)); } + return true; } template @@ -379,7 +392,10 @@ class bernoulli_numbers_cache if(start + n >= bn.size()) { std::size_t new_size = (std::min)((std::max)((std::max)(std::size_t(start + n), std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity())); - tangent_numbers_series(new_size); + if (!tangent_numbers_series(new_size)) + { + return std::fill_n(out, n, policies::raise_evaluation_error("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(start + n), pol)); + } } for(std::size_t i = (std::max)(std::size_t(max_bernoulli_b2n::value + 1), start); i < start + n; ++i) @@ -413,7 +429,8 @@ class bernoulli_numbers_cache if(start + n >= bn.size()) { std::size_t new_size = (std::min)((std::max)((std::max)(std::size_t(start + n), std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity())); - tangent_numbers_series(new_size); + if (!tangent_numbers_series(new_size)) + return std::fill_n(out, n, policies::raise_evaluation_error("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(new_size), pol)); } m_counter.store(static_cast(bn.size()), std::memory_order_release); } @@ -483,7 +500,8 @@ class bernoulli_numbers_cache if(start + n >= bn.size()) { std::size_t new_size = (std::min)((std::max)((std::max)(start + n, std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity())); - tangent_numbers_series(new_size); + if (!tangent_numbers_series(new_size)) + return std::fill_n(out, n, policies::raise_evaluation_error("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(start + n), pol)); } for(std::size_t i = start; i < start + n; ++i) @@ -527,7 +545,8 @@ class bernoulli_numbers_cache if(start + n >= bn.size()) { std::size_t new_size = (std::min)((std::max)((std::max)(start + n, std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity())); - tangent_numbers_series(new_size); + if (!tangent_numbers_series(new_size)) + return std::fill_n(out, n, policies::raise_evaluation_error("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(start + n), pol)); } m_counter.store(static_cast(bn.size()), std::memory_order_release); } From bf3b0258e88516266b2a28fb1a186c712981dd70 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 13 May 2022 19:25:18 +0100 Subject: [PATCH 32/60] Correct error handling in owens_t. --- include/boost/math/special_functions/owens_t.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/math/special_functions/owens_t.hpp b/include/boost/math/special_functions/owens_t.hpp index f96d6648e5..152b5cbdda 100644 --- a/include/boost/math/special_functions/owens_t.hpp +++ b/include/boost/math/special_functions/owens_t.hpp @@ -829,7 +829,7 @@ namespace boost val = owens_t_T6(h,a, pol); break; default: - BOOST_MATH_THROW_EXCEPTION(std::logic_error("selection routine in Owen's T function failed")); + val = policies::raise_evaluation_error("boost::math::owens_t", "selection routine in Owen's T function failed with h = %1%", h, pol); } return val; } From 6240f57892e14215df87a9b9f42e3db1fb4122db Mon Sep 17 00:00:00 2001 From: Christopher Kormanyos Date: Sat, 14 May 2022 09:52:44 +0200 Subject: [PATCH 33/60] Enclose a try/catch block within (no)-exceptions --- include/boost/math/special_functions/gamma.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/boost/math/special_functions/gamma.hpp b/include/boost/math/special_functions/gamma.hpp index b94dcbbc32..a3f6cfc4a5 100644 --- a/include/boost/math/special_functions/gamma.hpp +++ b/include/boost/math/special_functions/gamma.hpp @@ -1465,7 +1465,15 @@ T gamma_incomplete_imp(T a, T x, bool normalised, bool invert, result = pow(x, a) / (a); else { - try + #ifdef BOOST_NO_EXCEPTIONS + result = pow(x, a) / boost::math::tgamma(a + 1, pol); + using std::isfinite; + if(!(isfinite)(result)) + { + result = 0; + } + #else + try { result = pow(x, a) / boost::math::tgamma(a + 1, pol); } @@ -1473,6 +1481,7 @@ T gamma_incomplete_imp(T a, T x, bool normalised, bool invert, { result = 0; } + #endif } result *= 1 - a * x / (a + 1); if (p_derivative) From 7c5228a07bd911599d4f5e03f71fb220f1f0d963 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sun, 15 May 2022 17:35:11 +0100 Subject: [PATCH 34/60] Add no-exception handling tests. Add Ubuntu-22 plus gcc-12 and clang-14 CI tests. Fix a couple headers which still had noeh-unfriendly code. --- .github/workflows/ci.yml | 127 ++++++++++++++++++ .../detail/ibeta_inverse.hpp | 4 + .../boost/math/special_functions/gamma.hpp | 4 + test/Jamfile.v2 | 122 +++++++++++++++++ 4 files changed, 257 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c8b047f14..46022f33e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,70 @@ on: release: types: [published, created, edited] jobs: + ubuntu-jammy: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + compiler: [ g++-12, clang++-14 ] + standard: [ c++11, c++14, c++17, c++20 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Set TOOLSET + run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install g++-12 clang-14 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: ./bootstrap.sh + working-directory: ../boost-root + - name: Generate headers + run: ./b2 headers + working-directory: ../boost-root + - name: Generate user config + run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' + working-directory: ../boost-root + - name: Config info install + run: ../../../b2 config_info_travis_install toolset=$TOOLSET + working-directory: ../boost-root/libs/config/test + - name: Config info + run: ./config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER + working-directory: ../boost-root/libs/math/test ubuntu-focal: runs-on: ubuntu-20.04 strategy: @@ -77,6 +141,69 @@ jobs: - name: Test run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test + ubuntu-focal-no-eh: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + compiler: [ g++-9, g++-11, clang++-10 ] + standard: [ c++11, c++14, c++17, c++2a ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Set TOOLSET + run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install g++-9 g++-11 clang-9 clang-10 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: ./bootstrap.sh + working-directory: ../boost-root + - name: Generate headers + run: ./b2 headers + working-directory: ../boost-root + - name: Generate user config + run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' + working-directory: ../boost-root + - name: Config info install + run: ../../../b2 config_info_travis_install toolset=$TOOLSET + working-directory: ../boost-root/libs/config/test + - name: Config info + run: ./config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ../../../b2 toolset=$TOOLSET no_eh_tests exception-handling=off rtti=off define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER + working-directory: ../boost-root/libs/math/test ubuntu-bionic: runs-on: ubuntu-18.04 strategy: diff --git a/include/boost/math/special_functions/detail/ibeta_inverse.hpp b/include/boost/math/special_functions/detail/ibeta_inverse.hpp index e3dc0ba4fa..195f6abe18 100644 --- a/include/boost/math/special_functions/detail/ibeta_inverse.hpp +++ b/include/boost/math/special_functions/detail/ibeta_inverse.hpp @@ -688,13 +688,17 @@ T ibeta_inv_imp(T a, T b, T p, T q, const Policy& pol, T* py) T bet = 0; T xg; bool overflow = false; +#ifndef BOOST_NO_EXCEPTIONS try { +#endif bet = boost::math::beta(a, b, pol); +#ifndef BOOST_NO_EXCEPTIONS } catch (const std::runtime_error&) { overflow = true; } +#endif if (overflow || !(boost::math::isfinite)(bet)) { xg = exp((boost::math::lgamma(a + 1, pol) + boost::math::lgamma(b, pol) - boost::math::lgamma(a + b, pol) + log(p)) / a); diff --git a/include/boost/math/special_functions/gamma.hpp b/include/boost/math/special_functions/gamma.hpp index b94dcbbc32..13cd25e087 100644 --- a/include/boost/math/special_functions/gamma.hpp +++ b/include/boost/math/special_functions/gamma.hpp @@ -1465,14 +1465,18 @@ T gamma_incomplete_imp(T a, T x, bool normalised, bool invert, result = pow(x, a) / (a); else { +#ifndef BOOST_NO_EXCEPTIONS try { +#endif result = pow(x, a) / boost::math::tgamma(a + 1, pol); +#ifndef BOOST_NO_EXCEPTIONS } catch (const std::overflow_error&) { result = 0; } +#endif } result *= 1 - a * x / (a + 1); if (p_derivative) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 5deced8cb0..dddf47c879 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1504,3 +1504,125 @@ rule get_float128_tests } test-suite float128_tests : [ get_float128_tests ] ; + +# +# Things that we can test with exceptions and RTTI turned off: +# +alias no_eh_tests : + compl_abs_incl_test + compl_acos_incl_test + compl_acosh_incl_test + compl_asin_incl_test + compl_asinh_incl_test + compl_atan_incl_test + compl_atanh_incl_test + sf_acosh_incl_test + sf_asinh_incl_test + sf_atanh_incl_test + sf_beta_incl_test + sf_bernoulli_incl_test + sf_bessel_incl_test + sf_bessel_deriv_incl_test + sf_binomial_incl_test + sf_cbrt_incl_test + sf_cos_pi_incl_test + sf_digamma_incl_test + sf_polygamma_incl_test + sf_ellint_1_incl_test + sf_ellint_2_incl_test + sf_ellint_3_incl_test + sf_ellint_d_incl_test + sf_jacobi_theta_incl_test + sf_jacobi_zeta_incl_test + sf_heuman_lambda_incl_test + sf_ellint_rc_incl_test + sf_ellint_rd_incl_test + sf_ellint_rf_incl_test + sf_ellint_rj_incl_test + sf_ellint_rg_incl_test + sf_erf_incl_test + sf_expint_incl_test + sf_expm1_incl_test + sf_factorials_incl_test + sf_fpclassify_incl_test + sf_gamma_incl_test + sf_hermite_incl_test + sf_hypot_incl_test + sf_laguerre_incl_test + sf_lanczos_incl_test + sf_legendre_incl_test + #sf_legendre_stieltjes_incl_test + sf_log1p_incl_test + sf_math_fwd_incl_test + sf_modf_incl_test + sf_next_incl_test + sf_powm1_incl_test + sf_prime_incl_test + sf_relative_distance_incl_test + sf_round_incl_test + sf_sign_incl_test + sf_sin_pi_incl_test + sf_sinc_incl_test + sf_sinhc_incl_test + sf_sph_harm_incl_test + sf_sqrt1pm1_incl_test + sf_trunc_incl_test + sf_ulp_incl_test + sf_zeta_incl_test + sf_chebyshev_incl_test + sf_chebyshev_transform_incl_test + sf_fibonacci_incl_test + #sf_gegenbauer_incl_test + sf_lambert_w_incl_test + sf_nonfinite_num_facets_incl_test + sf_airy_incl_test + sf_hankel_incl_test + sf_jacobi_incl_test + sf_owens_t_incl_test + dist_skew_norm_incl_test + constants_incl_test + quad_trapezoidal_incl_test + test_traits + tools_config_inc_test + tools_fraction_inc_test + tools_minima_inc_test + tools_polynomial_inc_test + tools_precision_inc_test + tools_rational_inc_test + tools_real_cast_inc_test + tools_remez_inc_test + tools_roots_inc_test + tools_series_inc_test + tools_solve_inc_test + tools_stats_inc_test + tools_test_data_inc_test + #tools_test_inc_test + tools_toms748_inc_test + tools_agm_incl_test + tools_assert_incl_test + tools_atomic_incl_test + tools_big_constant_incl_test + #tools_centered_continued_fraction_incl_test + tools_cohen_acceleration_incl_test + tools_complex_incl_test + tools_condition_numbers_incl_test + tools_convert_from_string_incl_test + tools_cxx03_warn_incl_test + #tools_engel_expansion_incl_test + tools_header_deprecated_incl_test + tools_is_detected_incl_test + #tools_luroth_expansion_incl_test + tools_mp_incl_test + tools_norms_incl_test + tools_polynomial_gcd_incl_test + tools_promotion_incl_test + tools_random_vector_incl_test + #tools_simple_continued_fraction_incl_test + tools_test_value_incl_test + tools_throw_exception_incl_test + tools_traits_incl_test + #tools_ulps_plot_incl_test + tools_workaround_incl_test +; + +explicit no_eh_tests ; \ No newline at end of file From 6869066581b4736cb66df2bba49e7ab72ca521d4 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 16 May 2022 18:24:06 +0100 Subject: [PATCH 35/60] Disable chebeshev_transform testing in no-eh mode. Doesn't easily support exception handling free compilation. --- test/Jamfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index dddf47c879..2719709923 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1570,7 +1570,7 @@ alias no_eh_tests : sf_ulp_incl_test sf_zeta_incl_test sf_chebyshev_incl_test - sf_chebyshev_transform_incl_test + #sf_chebyshev_transform_incl_test sf_fibonacci_incl_test #sf_gegenbauer_incl_test sf_lambert_w_incl_test From 0edd1a23002656e977233258ab5e985251f6ca56 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 16 May 2022 18:25:23 +0100 Subject: [PATCH 36/60] Try building with -j2 in ubuntu-22 CI. Otherwise we see the VM abort with out-of-memory errors. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46022f33e7..f6d46ae6b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: run: ./config_info_travis working-directory: ../boost-root/libs/config/test - name: Test - run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER + run: ../../../b2 -j2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test ubuntu-focal: runs-on: ubuntu-20.04 From 990704e8a94054777a1072d0c9578983265515ff Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 23 May 2022 12:14:09 +0100 Subject: [PATCH 37/60] Correct #include logic in test_hyperexponential_dist.cpp. [CI SKIP] --- test/test_hyperexponential_dist.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/test_hyperexponential_dist.cpp b/test/test_hyperexponential_dist.cpp index 7a30e77de2..c7d3c9843b 100644 --- a/test/test_hyperexponential_dist.cpp +++ b/test/test_hyperexponential_dist.cpp @@ -374,6 +374,15 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(special_cases, RealT, test_types) BOOST_CHECK_CLOSE(boost::math::mode(hexp2), boost::math::mode(exp2), tol); } +// Test C++20 ranges (Currently only GCC10 has full support to P0896R4) +#if (__cplusplus > 202000L || _MSVC_LANG > 202000L) && __has_include() && __GNUC__ >= 10 +// Support for ranges is broken using gcc 11.1 +#if __GNUC__ != 11 +#include +#include +#endif +#endif + BOOST_AUTO_TEST_CASE_TEMPLATE(error_cases, RealT, test_types) { typedef boost::math::hyperexponential_distribution dist_t; @@ -393,8 +402,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(error_cases, RealT, test_types) #if (__cplusplus > 202000L || _MSVC_LANG > 202000L) && __has_include() && __GNUC__ >= 10 // Support for ranges is broken using gcc 11.1 #if __GNUC__ != 11 - #include - #include std::array probs_array {1,2}; std::array rates_array {1,2,3}; From 81269470d59c4c6f23175453e8541dd5f2d67691 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Tue, 24 May 2022 12:21:16 +0100 Subject: [PATCH 38/60] Split up 1F0 and 2F0 tests. Reduces compiler memory footprint of those test cases. --- test/Jamfile.v2 | 9 +++++++-- test/test_1F0.cpp | 6 ++++++ test/test_2F0.cpp | 9 ++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2719709923..c7b3a79b06 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -92,8 +92,13 @@ cpp-pch pch_light : pch_light.hpp : ../../test/build//boost_unit_test_frame lib compile_test_main : compile_test/main.cpp ; test-suite special_fun : - [ run test_1F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] ] # hypergeometric_pFq_checked_series.hpp uses auto, the rest are from quadrature tests. - [ run test_2F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] ] + [ run test_1F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] TEST=1 : test_1F0_1 ] # hypergeometric_pFq_checked_series.hpp uses auto, the rest are from quadrature tests. + [ run test_1F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] TEST=2 : test_1F0_2 ] # hypergeometric_pFq_checked_series.hpp uses auto, the rest are from quadrature tests. + [ run test_1F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] TEST=3 : test_1F0_3 ] # hypergeometric_pFq_checked_series.hpp uses auto, the rest are from quadrature tests. + [ run test_2F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] TEST=1 : test_2F0_1 ] + [ run test_2F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] TEST=2 : test_2F0_2 ] + [ run test_2F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] TEST=3 : test_2F0_3 ] + [ run test_2F0.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] TEST=4 : test_2F0_4 ] [ run test_0F1.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] TEST=1 : test_0F1_1 ] [ run test_0F1.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] TEST=2 : test_0F1_2 ] diff --git a/test/test_1F0.cpp b/test/test_1F0.cpp index f99881da70..c32f253928 100644 --- a/test/test_1F0.cpp +++ b/test/test_1F0.cpp @@ -11,6 +11,7 @@ BOOST_AUTO_TEST_CASE( test_main ) { +#if !defined(TEST) || (TEST == 1) #ifndef BOOST_MATH_BUGGY_LARGE_FLOAT_CONSTANTS test_spots(0.0F); #endif @@ -21,8 +22,13 @@ BOOST_AUTO_TEST_CASE( test_main ) test_spots(boost::math::concepts::real_concept(0.1)); #endif #endif +#endif +#if !defined(TEST) || (TEST == 2) test_spots(boost::multiprecision::cpp_bin_float_quad()); +#endif +#if !defined(TEST) || (TEST == 3) test_spots(boost::multiprecision::cpp_dec_float_50()); +#endif } diff --git a/test/test_2F0.cpp b/test/test_2F0.cpp index 66b2fd9fb1..988ed9c0bd 100644 --- a/test/test_2F0.cpp +++ b/test/test_2F0.cpp @@ -82,21 +82,28 @@ BOOST_AUTO_TEST_CASE( test_main ) { expected_results(); BOOST_MATH_CONTROL_FP; - +#if !defined(TEST) || (TEST == 1) #ifndef BOOST_MATH_BUGGY_LARGE_FLOAT_CONSTANTS test_spots(0.0F, "float"); #endif test_spots(0.0, "double"); +#endif +#if !defined(TEST) || (TEST == 2) #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS test_spots(0.0L, "long double"); #ifndef BOOST_MATH_NO_REAL_CONCEPT_TESTS test_spots(boost::math::concepts::real_concept(0.1), "real_concept"); #endif #endif +#endif #ifndef BOOST_MATH_NO_MP_TESTS using dec_40 = boost::multiprecision::number>; +#if !defined(TEST) || (TEST == 3) test_spots(boost::multiprecision::cpp_bin_float_quad(), "cpp_bin_float_quad"); +#endif +#if !defined(TEST) || (TEST == 4) test_spots(dec_40(), "dec_40"); #endif +#endif } From 536851add44d5890c16c3e39b875ddcab2bc013f Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Tue, 24 May 2022 16:47:48 +0100 Subject: [PATCH 39/60] Change naked throws to BOOST_MATH_THROW_EXCEPTION. --- include/boost/math/special_functions/chebyshev.hpp | 2 +- include/boost/math/tools/polynomial.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/math/special_functions/chebyshev.hpp b/include/boost/math/special_functions/chebyshev.hpp index 66356ee012..2ea1560986 100644 --- a/include/boost/math/special_functions/chebyshev.hpp +++ b/include/boost/math/special_functions/chebyshev.hpp @@ -271,7 +271,7 @@ inline Real chebyshev_clenshaw_recurrence(const Real* const c, size_t length, co { if (x < a || x > b) { - throw std::domain_error("x in [a, b] is required."); + BOOST_MATH_THROW_EXCEPTION(std::domain_error("x in [a, b] is required.")); } if (length < 2) { diff --git a/include/boost/math/tools/polynomial.hpp b/include/boost/math/tools/polynomial.hpp index c2528b1da7..17e4362de2 100644 --- a/include/boost/math/tools/polynomial.hpp +++ b/include/boost/math/tools/polynomial.hpp @@ -360,7 +360,7 @@ class polynomial size_type degree() const { if (size() == 0) - throw std::logic_error("degree() is undefined for the zero polynomial."); + BOOST_MATH_THROW_EXCEPTION(std::logic_error("degree() is undefined for the zero polynomial.")); return m_data.size() - 1; } value_type& operator[](size_type i) From e5eae18f14e40e8eae453457cc257d316c7fffc4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 25 May 2022 08:13:24 -0700 Subject: [PATCH 40/60] Chatterjee Correlation Coefficient (#770) * Implement rank vector [ci skip] * Add documentation. Admittedly terrible. * Add unit tests. * Cleanup method of detecting if execution policies are valid or not [ci skip] * Implement and test chatterjee correlation [ci skip] * Add spot checks and special handling for constant Y [ci skip] * Add performance file [ci skip] * Add execution policy support to rank [ci skip] * Remove duplicates from v when generating the order vector [ci skip] * Fix macro error for use of [ci skip] * Use explicit types instead of auto to avoid warnings [ci skip] * Add execution policy testing to rank [ci skip] * Add threaded implementation [ci skip] * Added threaded testing * Fix formatting and ASCII issues in test * Fix more ASCII issues * refactoring * Fix threaded impl * Remove non-ASCII apostrophe [ci skip] * Doc fixes and add test comparing generally to paper values * Significantly tighten tolerance around expected values from paper * Change tolerance for sin comparison Co-authored-by: Nick Thompson --- doc/statistics/chatterjee_correlation.qbk | 74 ++++++++ .../math/statistics/bivariate_statistics.hpp | 15 +- .../statistics/chatterjee_correlation.hpp | 159 ++++++++++++++++++ include/boost/math/statistics/detail/rank.hpp | 140 +++++++++++++++ .../math/statistics/univariate_statistics.hpp | 6 +- include/boost/math/tools/config.hpp | 6 + include/boost/math/tools/random_vector.hpp | 26 ++- .../chatterjee_correlation_performance.cpp | 34 ++++ test/Jamfile.v2 | 2 + .../stats_chaterjee_incl_test.cpp | 9 + test/math_unit_test.hpp | 2 + test/test_chatterjee_correlation.cpp | 159 ++++++++++++++++++ test/test_rank.cpp | 102 +++++++++++ 13 files changed, 719 insertions(+), 15 deletions(-) create mode 100644 doc/statistics/chatterjee_correlation.qbk create mode 100644 include/boost/math/statistics/chatterjee_correlation.hpp create mode 100644 include/boost/math/statistics/detail/rank.hpp create mode 100644 reporting/performance/chatterjee_correlation_performance.cpp create mode 100644 test/compile_test/stats_chaterjee_incl_test.cpp create mode 100644 test/test_chatterjee_correlation.cpp create mode 100644 test/test_rank.cpp diff --git a/doc/statistics/chatterjee_correlation.qbk b/doc/statistics/chatterjee_correlation.qbk new file mode 100644 index 0000000000..520ab0cc6b --- /dev/null +++ b/doc/statistics/chatterjee_correlation.qbk @@ -0,0 +1,74 @@ +[/ + Copyright 2022 Matt Borland + + Distributed under 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). +] + +[section:chatterjee_correlation Chatterjee Correlation] + +[heading Synopsis] + +`` +#include + +namespace boost::math::statistics { + + C++17: + template + auto chatterjee_correlation(ExecutionPolicy&& exec, const Container& u, const Container& v); + + C++11: + template + auto chatterjee_correlation(const Container& u, const Container& v); +} +`` + +[heading Description] + +The classical correlation coefficients like the Pearson's correlation are useful primarily for distinguishing when one dataset depends linearly on another. +However, Pearson's correlation coefficient has a known weakness in that when the dependent variable has an obvious functional relationship with the independent variable, the value of the correlation coefficient can take on any value. +As Chatterjee says: + +> Ideally, one would like a coefficient that approaches +its maximum value if and only if one variable looks more and more like a +noiseless function of the other, just as Pearson correlation is close to its maximum value if and only if one variable is close to being a noiseless linear function of the other. + +This is the problem Chatterjee's coefficient solves. +Let X and Y be random variables, where Y is not constant, and let (X_i, Y_i) be samples from this distribution. +Rearrange these samples so that X_(0) < X_{(1)} < ... X_{(n-1)} and create (R(X_{(i)}), R(Y_{(i)})). +The Chatterjee correlation is then given by + +[$../equations/chatterjee_correlation.svg] + +In the limit of an infinite amount of i.i.d data, the statistic lies in [0, 1]. +However, if the data is not infinite, the statistic may be negative. +If X and Y are independent, the value is zero, and if Y is a measurable function of X, then the statistic is unity. +The complexity is O(n log n). + +An example is given below: + + std::vector X{1,2,3,4,5}; + std::vector Y{1,2,3,4,5}; + using boost::math::statistics::chatterjee_correlation; + double coeff = chatterjee_correlation(X, Y); + +The implementation follows [@https://arxiv.org/pdf/1909.10140.pdf Chatterjee's paper]. + +/Nota bene:/ If the input is an integer type the output will be a double precision type. + +[heading Invariants] + +The function expects at least two samples, a non-constant vector Y, and the same number of X's as Y's. +If Y is constant, the result is a quiet NaN. +The data set must be sorted by X values. +If there are ties in the values of X, then the statistic is random due to the random breaking of ties. +Of course, random numbers are not used internally, but the result is not guaranteed to be identical on different systems. + +[heading References] + +* Chatterjee, Sourav. "A new coefficient of correlation." Journal of the American Statistical Association 116.536 (2021): 2009-2022. + +[endsect] +[/section:chatterjee_correlation Chatterjee Correlation] diff --git a/include/boost/math/statistics/bivariate_statistics.hpp b/include/boost/math/statistics/bivariate_statistics.hpp index 1854898139..d6f91faa93 100644 --- a/include/boost/math/statistics/bivariate_statistics.hpp +++ b/include/boost/math/statistics/bivariate_statistics.hpp @@ -18,13 +18,10 @@ #include #include -// Support compilers with P0024R2 implemented without linking TBB -// https://en.cppreference.com/w/cpp/compiler_support -#if !defined(BOOST_NO_CXX17_HDR_EXECUTION) && defined(BOOST_HAS_THREADS) +#ifdef BOOST_MATH_EXEC_COMPATIBLE #include #include #include -#define EXEC_COMPATIBLE #endif namespace boost{ namespace math{ namespace statistics { namespace detail { @@ -60,7 +57,7 @@ ReturnType means_and_covariance_seq_impl(ForwardIterator u_begin, ForwardIterato return std::make_tuple(mu_u, mu_v, cov/i, Real(i)); } -#ifdef EXEC_COMPATIBLE +#ifdef BOOST_MATH_EXEC_COMPATIBLE // Numerically stable parallel computation of (co-)variance // https://dl.acm.org/doi/10.1145/3221269.3223036 @@ -154,7 +151,7 @@ ReturnType means_and_covariance_parallel_impl(ForwardIterator u_begin, ForwardIt return std::make_tuple(mu_u_a, mu_v_a, cov_a, n_a); } -#endif // EXEC_COMPATIBLE +#endif // BOOST_MATH_EXEC_COMPATIBLE template ReturnType correlation_coefficient_seq_impl(ForwardIterator u_begin, ForwardIterator u_end, ForwardIterator v_begin, ForwardIterator v_end) @@ -204,7 +201,7 @@ ReturnType correlation_coefficient_seq_impl(ForwardIterator u_begin, ForwardIter return std::make_tuple(mu_u, Qu, mu_v, Qv, cov, rho, Real(i)); } -#ifdef EXEC_COMPATIBLE +#ifdef BOOST_MATH_EXEC_COMPATIBLE // Numerically stable parallel computation of (co-)variance: // https://dl.acm.org/doi/10.1145/3221269.3223036 @@ -324,11 +321,11 @@ ReturnType correlation_coefficient_parallel_impl(ForwardIterator u_begin, Forwar return std::make_tuple(mu_u_a, Qu_a, mu_v_a, Qv_a, cov_a, rho, n_a); } -#endif // EXEC_COMPATIBLE +#endif // BOOST_MATH_EXEC_COMPATIBLE } // namespace detail -#ifdef EXEC_COMPATIBLE +#ifdef BOOST_MATH_EXEC_COMPATIBLE template inline auto means_and_covariance(ExecutionPolicy&& exec, Container const & u, Container const & v) diff --git a/include/boost/math/statistics/chatterjee_correlation.hpp b/include/boost/math/statistics/chatterjee_correlation.hpp new file mode 100644 index 0000000000..ad0b33a429 --- /dev/null +++ b/include/boost/math/statistics/chatterjee_correlation.hpp @@ -0,0 +1,159 @@ +// (C) Copyright Matt Borland 2022. +// 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) + +#ifndef BOOST_MATH_STATISTICS_CHATTERJEE_CORRELATION_HPP +#define BOOST_MATH_STATISTICS_CHATTERJEE_CORRELATION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_MATH_EXEC_COMPATIBLE +#include +#include +#include +#endif + +namespace boost { namespace math { namespace statistics { + +namespace detail { + +template +std::size_t chatterjee_transform(BDIter begin, BDIter end) +{ + std::size_t sum = 0; + + while(++begin != end) + { + if(*begin > *std::prev(begin)) + { + sum += *begin - *std::prev(begin); + } + else + { + sum += *std::prev(begin) - *begin; + } + } + + return sum; +} + +template +ReturnType chatterjee_correlation_seq_impl(ForwardIterator u_begin, ForwardIterator u_end, ForwardIterator v_begin, ForwardIterator v_end) +{ + using std::abs; + + BOOST_MATH_ASSERT_MSG(std::is_sorted(u_begin, u_end), "The x values must be sorted in order to use this functionality"); + + const std::vector rank_vector = rank(v_begin, v_end); + + std::size_t sum = chatterjee_transform(rank_vector.begin(), rank_vector.end()); + + ReturnType result = static_cast(1) - (static_cast(3 * sum) / static_cast(rank_vector.size() * rank_vector.size() - 1)); + + // If the result is 1 then Y is constant and all the elements must be ties + if (abs(result - static_cast(1)) < std::numeric_limits::epsilon()) + { + return std::numeric_limits::quiet_NaN(); + } + + return result; +} + +} // Namespace detail + +template ::value, double, Real>::type> +inline ReturnType chatterjee_correlation(const Container& u, const Container& v) +{ + return detail::chatterjee_correlation_seq_impl(std::begin(u), std::end(u), std::begin(v), std::end(v)); +} + +}}} // Namespace boost::math::statistics + +#ifdef BOOST_MATH_EXEC_COMPATIBLE + +namespace boost::math::statistics { + +namespace detail { + +template +ReturnType chatterjee_correlation_par_impl(ExecutionPolicy&& exec, ForwardIterator u_begin, ForwardIterator u_end, + ForwardIterator v_begin, ForwardIterator v_end) +{ + using std::abs; + BOOST_MATH_ASSERT_MSG(std::is_sorted(std::forward(exec), u_begin, u_end), "The x values must be sorted in order to use this functionality"); + + auto rank_vector = rank(std::forward(exec), v_begin, v_end); + + const auto num_threads = std::thread::hardware_concurrency() == 0 ? 2u : std::thread::hardware_concurrency(); + std::vector> future_manager {}; + const auto elements_per_thread = std::ceil(static_cast(rank_vector.size()) / num_threads); + + auto it = rank_vector.begin(); + auto end = rank_vector.end(); + for(std::size_t i {}; i < num_threads - 1; ++i) + { + future_manager.emplace_back(std::async(std::launch::async | std::launch::deferred, [it, elements_per_thread]() -> std::size_t + { + return chatterjee_transform(it, std::next(it, elements_per_thread)); + })); + it = std::next(it, elements_per_thread - 1); + } + + future_manager.emplace_back(std::async(std::launch::async | std::launch::deferred, [it, end]() -> std::size_t + { + return chatterjee_transform(it, end); + })); + + std::size_t sum {}; + for(std::size_t i {}; i < future_manager.size(); ++i) + { + sum += future_manager[i].get(); + } + + ReturnType result = static_cast(1) - (static_cast(3 * sum) / static_cast(rank_vector.size() * rank_vector.size() - 1)); + + // If the result is 1 then Y is constant and all the elements must be ties + if (abs(result - static_cast(1)) < std::numeric_limits::epsilon()) + { + return std::numeric_limits::quiet_NaN(); + } + + return result; +} + +} // Namespace detail + +template , double, Real>> +inline ReturnType chatterjee_correlation(ExecutionPolicy&& exec, const Container& u, const Container& v) +{ + if constexpr (std::is_same_v, decltype(std::execution::seq)>) + { + return detail::chatterjee_correlation_seq_impl(std::cbegin(u), std::cend(u), + std::cbegin(v), std::cend(v)); + } + else + { + return detail::chatterjee_correlation_par_impl(std::forward(exec), + std::cbegin(u), std::cend(u), + std::cbegin(v), std::cend(v)); + } +} + +} // Namespace boost::math::statistics + +#endif + +#endif // BOOST_MATH_STATISTICS_CHATTERJEE_CORRELATION_HPP diff --git a/include/boost/math/statistics/detail/rank.hpp b/include/boost/math/statistics/detail/rank.hpp new file mode 100644 index 0000000000..4e5211607e --- /dev/null +++ b/include/boost/math/statistics/detail/rank.hpp @@ -0,0 +1,140 @@ +// (C) Copyright Matt Borland 2022 +// 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) + +#ifndef BOOST_MATH_STATISTICS_DETAIL_RANK_HPP +#define BOOST_MATH_STATISTICS_DETAIL_RANK_HPP + +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_MATH_EXEC_COMPATIBLE +#include +#endif + +namespace boost { namespace math { namespace statistics { namespace detail { + +struct pair_equal +{ + template + bool operator()(const std::pair& a, const std::pair& b) const + { + return a.first == b.first; + } +}; + +}}}} // Namespaces + +#ifndef BOOST_MATH_EXEC_COMPATIBLE + +namespace boost { namespace math { namespace statistics { namespace detail { + +template ::value_type> +auto rank(ForwardIterator first, ForwardIterator last) -> std::vector +{ + std::size_t elements = std::distance(first, last); + + std::vector> rank_vector(elements); + std::size_t i = 0; + while (first != last) + { + rank_vector[i] = std::make_pair(*first, i); + ++i; + ++first; + } + + std::sort(rank_vector.begin(), rank_vector.end()); + + // Remove duplicates + rank_vector.erase(std::unique(rank_vector.begin(), rank_vector.end(), pair_equal()), rank_vector.end()); + elements = rank_vector.size(); + + std::pair rank; + std::vector result(elements); + for (i = 0; i < elements; ++i) + { + if (rank_vector[i].first != rank.first) + { + rank = std::make_pair(rank_vector[i].first, i); + } + result[rank_vector[i].second] = rank.second; + } + + return result; +} + +template +inline auto rank(const Container& c) -> std::vector +{ + return rank(std::begin(c), std::end(c)); +} + +}}}} // Namespaces + +#else + +namespace boost::math::statistics::detail { + +template ::value_type> +auto rank(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last) +{ + std::size_t elements = std::distance(first, last); + + std::vector> rank_vector(elements); + std::size_t i = 0; + while (first != last) + { + rank_vector[i] = std::make_pair(*first, i); + ++i; + ++first; + } + + std::sort(exec, rank_vector.begin(), rank_vector.end()); + + // Remove duplicates + rank_vector.erase(std::unique(exec, rank_vector.begin(), rank_vector.end(), pair_equal()), rank_vector.end()); + elements = rank_vector.size(); + + std::pair rank; + std::vector result(elements); + for (i = 0; i < elements; ++i) + { + if (rank_vector[i].first != rank.first) + { + rank = std::make_pair(rank_vector[i].first, i); + } + result[rank_vector[i].second] = rank.second; + } + + return result; +} + +template +inline auto rank(ExecutionPolicy&& exec, const Container& c) +{ + return rank(exec, std::cbegin(c), std::cend(c)); +} + +template ::value_type> +inline auto rank(ForwardIterator first, ForwardIterator last) +{ + return rank(std::execution::seq, first, last); +} + +template +inline auto rank(const Container& c) +{ + return rank(std::execution::seq, std::cbegin(c), std::cend(c)); +} + +} // Namespaces + +#endif // BOOST_MATH_EXEC_COMPATIBLE + +#endif // BOOST_MATH_STATISTICS_DETAIL_RANK_HPP diff --git a/include/boost/math/statistics/univariate_statistics.hpp b/include/boost/math/statistics/univariate_statistics.hpp index 63b0bd7701..711d807c79 100644 --- a/include/boost/math/statistics/univariate_statistics.hpp +++ b/include/boost/math/statistics/univariate_statistics.hpp @@ -20,9 +20,7 @@ #include #include -// Support compilers with P0024R2 implemented without linking TBB -// https://en.cppreference.com/w/cpp/compiler_support -#if !defined(BOOST_NO_CXX17_HDR_EXECUTION) && defined(BOOST_HAS_THREADS) +#ifdef BOOST_MATH_EXEC_COMPATIBLE #include namespace boost::math::statistics { @@ -672,7 +670,7 @@ inline auto mode(Container & v) } // Namespace boost::math::statistics -#else // Backwards compatible bindings for C++11 +#else // Backwards compatible bindings for C++11 or execution is not implemented namespace boost { namespace math { namespace statistics { diff --git a/include/boost/math/tools/config.hpp b/include/boost/math/tools/config.hpp index 2a054fc261..d809338cc6 100644 --- a/include/boost/math/tools/config.hpp +++ b/include/boost/math/tools/config.hpp @@ -79,6 +79,12 @@ #endif // BOOST_MATH_STANDALONE +// Support compilers with P0024R2 implemented without linking TBB +// https://en.cppreference.com/w/cpp/compiler_support +#if !defined(BOOST_NO_CXX17_HDR_EXECUTION) && defined(BOOST_HAS_THREADS) +# define BOOST_MATH_EXEC_COMPATIBLE +#endif + #include // for min and max #include #include diff --git a/include/boost/math/tools/random_vector.hpp b/include/boost/math/tools/random_vector.hpp index 8e3a3289aa..d1954ee3b5 100644 --- a/include/boost/math/tools/random_vector.hpp +++ b/include/boost/math/tools/random_vector.hpp @@ -12,8 +12,8 @@ namespace boost { namespace math { // To stress test, set global_seed = 0, global_size = huge. -static const std::size_t global_seed = 0; -static const std::size_t global_size = 128; +static constexpr std::size_t global_seed = 0; +static constexpr std::size_t global_size = 128; template::value, bool>::type = true> std::vector generate_random_vector(std::size_t size, std::size_t seed) @@ -35,6 +35,28 @@ std::vector generate_random_vector(std::size_t size, std::size_t seed) return v; } +template::value, bool>::type = true> +std::vector generate_random_uniform_vector(std::size_t size, std::size_t seed, T lower_bound = T(0), T upper_bound = T(1)) +{ + if (seed == 0) + { + std::random_device rd; + seed = rd(); + } + std::vector v(size); + + std::mt19937 gen(seed); + + std::uniform_real_distribution dis(lower_bound, upper_bound); + + for (auto& i : v) + { + i = dis(gen); + } + + return v; +} + template::value, bool>::type = true> std::vector generate_random_vector(std::size_t size, std::size_t seed, T mean, T stddev) { diff --git a/reporting/performance/chatterjee_correlation_performance.cpp b/reporting/performance/chatterjee_correlation_performance.cpp new file mode 100644 index 0000000000..866edb1048 --- /dev/null +++ b/reporting/performance/chatterjee_correlation_performance.cpp @@ -0,0 +1,34 @@ +// (C) Copyright Matt Borland 2022. +// 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 +#include +#include +#include +#include + +using boost::math::generate_random_vector; + +template +void chatterjee_correlation(benchmark::State& state) +{ + constexpr std::size_t seed {}; + const std::size_t size = state.range(0); + std::vector u = generate_random_vector(size, seed); + std::vector v = generate_random_vector(size, seed); + + std::sort(u.begin(), u.end()); + + for (auto _ : state) + { + benchmark::DoNotOptimize(boost::math::statistics::chatterjee_correlation(u, v)); + } + state.SetComplexityN(state.range(0)); +} + +BENCHMARK_TEMPLATE(chatterjee_correlation, float)->RangeMultiplier(2)->Range(1 << 6, 1 << 20)->Complexity()->UseRealTime(); +BENCHMARK_TEMPLATE(chatterjee_correlation, double)->RangeMultiplier(2)->Range(1 << 6, 1 << 20)->Complexity()->UseRealTime(); + +BENCHMARK_MAIN(); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 5deced8cb0..f3df8c2c4f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1012,6 +1012,8 @@ test-suite misc : [ run bivariate_statistics_test.cpp : : : [ requires cxx11_hdr_forward_list cxx11_hdr_atomic cxx11_hdr_thread cxx11_hdr_tuple cxx11_hdr_future cxx11_sfinae_expr ] [ check-target-builds ../config//is_cygwin_run "Cygwin CI run" : no ] ] [ run linear_regression_test.cpp : : : [ requires cxx11_hdr_forward_list cxx11_hdr_atomic cxx11_hdr_thread cxx11_hdr_tuple cxx11_hdr_future cxx11_sfinae_expr ] ] [ run test_runs_test.cpp : : : [ requires cxx17_if_constexpr cxx17_std_apply ] ] + [ run test_chatterjee_correlation.cpp ../../test/build//boost_unit_test_framework ] + [ run test_rank.cpp ../../test/build//boost_unit_test_framework ] [ run lanczos_smoothing_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr cxx17_std_apply ] ] [ run condition_number_test.cpp ../../test/build//boost_unit_test_framework : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : "-Bstatic -lquadmath -Bdynamic" ] [ requires cxx17_if_constexpr cxx17_std_apply ] ] [ run test_real_concept.cpp ../../test/build//boost_unit_test_framework ] diff --git a/test/compile_test/stats_chaterjee_incl_test.cpp b/test/compile_test/stats_chaterjee_incl_test.cpp new file mode 100644 index 0000000000..84c235c244 --- /dev/null +++ b/test/compile_test/stats_chaterjee_incl_test.cpp @@ -0,0 +1,9 @@ +// Copyright Matt Borland 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) +// +// Basic sanity check that header +// #includes all the files that it needs to. +// +#include diff --git a/test/math_unit_test.hpp b/test/math_unit_test.hpp index 6a6af9fc15..0a526a15a3 100644 --- a/test/math_unit_test.hpp +++ b/test/math_unit_test.hpp @@ -384,6 +384,8 @@ int report_errors() #define CHECK_ULP_CLOSE(X, Y, Z) boost::math::test::check_ulp_close((X), (Y), (Z), __FILE__, __func__, __LINE__) +#define CHECK_GE(X, Y) boost::math::test::check_le((Y), (X), __FILE__, __func__, __LINE__) + #define CHECK_LE(X, Y) boost::math::test::check_le((X), (Y), __FILE__, __func__, __LINE__) #define CHECK_NAN(X) boost::math::test::check_nan((X), __FILE__, __func__, __LINE__) diff --git a/test/test_chatterjee_correlation.cpp b/test/test_chatterjee_correlation.cpp new file mode 100644 index 0000000000..be91a925e6 --- /dev/null +++ b/test/test_chatterjee_correlation.cpp @@ -0,0 +1,159 @@ +// (C) Copyright Matt Borland 2022. +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "math_unit_test.hpp" + +// The Chatterjee correlation is invariant under: +// - Shuffles. (X_i, Y_i) -> (X_sigma(i), Y_sigma(i)), where sigma is a permutation. +// - Strictly monotone transformations: (X_i, Y_i) -> (f(X_i), g(Y_i)) where f' > 0 and g' > 0. +// + +using boost::math::statistics::chatterjee_correlation; + +template +void properties() +{ + std::size_t vector_size = 256; + std::mt19937_64 mt(123521); + std::uniform_real_distribution unif(-1, 1); + std::vector X(vector_size); + std::vector Y(vector_size); + + for (std::size_t i = 0; i < vector_size; ++i) + { + X[i] = unif(mt); + Y[i] = unif(mt); + } + std::sort(X.begin(), X.end()); + Real coeff1 = chatterjee_correlation(X, Y); + // The minimum possible value of En(X, Y) is -1/2 + O(1/n) + CHECK_GE(coeff1, Real(-0.5)); + CHECK_LE(coeff1, Real(1)); + + // Now apply a monotone function to the data + for (std::size_t i = 0; i < vector_size; ++i) + { + X[i] = Real(2.3)*X[i] - Real(7.3); + Y[i] = Real(7.6)*Y[i] - Real(8.6); + } + auto coeff3 = chatterjee_correlation(X, Y); + CHECK_EQUAL(coeff1, coeff3); + + // If there are no ties among the Yis, the maximum possible value of Xi(X, Y) is (n - 2)/(n + 1), which is attained if Yi = Xi for all i + auto coeff = chatterjee_correlation(X, X); + // These floating point numbers are computed by two different methods, so we can expect some floating point error: + const auto n = X.size(); + CHECK_ULP_CLOSE(coeff, Real(n-2)/Real(n+1), 1); + std::sort(Y.begin(), Y.end()); + coeff = chatterjee_correlation(Y, Y); + CHECK_ULP_CLOSE(coeff, Real(n-2)/Real(n+1), 1); +} + +template +void test_spots() +{ + // Rank Order: Result will be 1 - 3*3 / (4^2 - 1) = 1 - 9/15 = 0.6 + std::vector x = {1, 2, 3, 4}; + std::vector y = {1, 2, 3, 4}; + CHECK_ULP_CLOSE(chatterjee_correlation(x, y), 1 - Real(9)/15, 1); + + // Reverse rank order should be the same as above + y = {4, 3, 2, 1}; + CHECK_ULP_CLOSE(chatterjee_correlation(x, y), 1 - Real(9)/15, 1); + + // Alternating order: 1 - 3*5 / (4^2 - 1) = 1 - 15/15 = 0 + y = {1, 3, 2, 4}; + CHECK_ULP_CLOSE(chatterjee_correlation(x, y), Real(0), 1); + + // All ties will yield quiet NaN + y = {1, 1, 1, 1}; + CHECK_NAN(chatterjee_correlation(x, y)); +} + +#ifdef BOOST_MATH_EXEC_COMPATIBLE + +template +void test_threaded(ExecutionPolicy&& exec) +{ + std::vector x = boost::math::generate_random_vector(1024, 0); + std::vector y = boost::math::generate_random_vector(1024, 1); + + std::sort(std::forward(exec), x.begin(), x.end()); + + auto seq_ans = chatterjee_correlation(x, y); + auto par_ans = chatterjee_correlation(exec, x, y); + + CHECK_ULP_CLOSE(seq_ans, par_ans, 1); +}; + +#endif // BOOST_MATH_EXEC_COMPATIBLE + +template +void test_paper() +{ + constexpr Real two_pi = boost::math::constants::two_pi(); + + // Page 9 figure (a) y = x + std::vector x = boost::math::generate_random_uniform_vector(100, 0, -two_pi, two_pi); + std::sort(x.begin(), x.end()); + auto result = chatterjee_correlation(x, x); + CHECK_MOLLIFIED_CLOSE(result, Real(0.970), 0.005); + + // Page 9 figure (d) y = x^2 + std::vector y = x; + for (auto& i : y) + { + i *= i; + } + + result = chatterjee_correlation(x, y); + CHECK_MOLLIFIED_CLOSE(result, Real(0.941), 0.005); + + // Page 9 figure (g) y = sin(x) + for (std::size_t i {}; i < x.size(); ++i) + { + y[i] = std::sin(x[i]); + } + + result = chatterjee_correlation(x, y); + CHECK_MOLLIFIED_CLOSE(result, Real(0.885), 0.01); +} + +int main(void) +{ + properties(); + properties(); + properties(); + + test_spots(); + test_spots(); + test_spots(); + + #ifdef BOOST_MATH_EXEC_COMPATIBLE + + test_threaded(std::execution::par); + test_threaded(std::execution::par); + test_threaded(std::execution::par); + test_threaded(std::execution::par_unseq); + test_threaded(std::execution::par_unseq); + test_threaded(std::execution::par_unseq); + + #endif // BOOST_MATH_EXEC_COMPATIBLE + + test_paper(); + test_paper(); + test_paper(); + + return boost::math::test::report_errors(); +} diff --git a/test/test_rank.cpp b/test/test_rank.cpp new file mode 100644 index 0000000000..73c022121d --- /dev/null +++ b/test/test_rank.cpp @@ -0,0 +1,102 @@ +// (C) Copyright Matt Borland 2022. +// 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 +#include +#include +#include +#include "math_unit_test.hpp" + +template +void test() +{ + std::vector test_vals {T(1.0), T(3.2), T(2.4), T(5.6), T(4.1)}; + auto rank_vector = boost::math::statistics::detail::rank(test_vals.begin(), test_vals.end()); + + CHECK_EQUAL(static_cast(0), rank_vector[0]); + CHECK_EQUAL(static_cast(2), rank_vector[1]); + CHECK_EQUAL(static_cast(1), rank_vector[2]); + CHECK_EQUAL(static_cast(4), rank_vector[3]); + CHECK_EQUAL(static_cast(3), rank_vector[4]); + + // Remove duplicates + test_vals.push_back(T(4.1)); + test_vals.push_back(T(2.4)); + rank_vector = boost::math::statistics::detail::rank(test_vals.begin(), test_vals.end()); + + // Check the size is correct and the ordering is not disrupted + CHECK_EQUAL(static_cast(5), rank_vector.size()); + CHECK_EQUAL(static_cast(0), rank_vector[0]); + CHECK_EQUAL(static_cast(2), rank_vector[1]); + CHECK_EQUAL(static_cast(1), rank_vector[2]); + CHECK_EQUAL(static_cast(4), rank_vector[3]); + CHECK_EQUAL(static_cast(3), rank_vector[4]); +} + +template +void container_test() +{ + std::vector test_vals {T(1.0), T(3.2), T(2.4), T(5.6), T(4.1)}; + auto rank_vector = boost::math::statistics::detail::rank(test_vals); + + CHECK_EQUAL(static_cast(0), rank_vector[0]); + CHECK_EQUAL(static_cast(2), rank_vector[1]); + CHECK_EQUAL(static_cast(1), rank_vector[2]); + CHECK_EQUAL(static_cast(4), rank_vector[3]); + CHECK_EQUAL(static_cast(3), rank_vector[4]); +} + +#ifdef BOOST_MATH_EXEC_COMPATIBLE + +#include + +template +void execution_test(ExecutionPolicy&& exec) +{ + std::vector test_vals {T(1.0), T(3.2), T(2.4), T(5.6), T(4.1)}; + auto rank_vector = boost::math::statistics::detail::rank(exec, test_vals.begin(), test_vals.end()); + + CHECK_EQUAL(static_cast(0), rank_vector[0]); + CHECK_EQUAL(static_cast(2), rank_vector[1]); + CHECK_EQUAL(static_cast(1), rank_vector[2]); + CHECK_EQUAL(static_cast(4), rank_vector[3]); + CHECK_EQUAL(static_cast(3), rank_vector[4]); + + // Remove duplicates + test_vals.push_back(T(4.1)); + test_vals.push_back(T(2.4)); + rank_vector = boost::math::statistics::detail::rank(exec, test_vals.begin(), test_vals.end()); + + // Check the size is correct and the ordering is not disrupted + CHECK_EQUAL(static_cast(5), rank_vector.size()); + CHECK_EQUAL(static_cast(0), rank_vector[0]); + CHECK_EQUAL(static_cast(2), rank_vector[1]); + CHECK_EQUAL(static_cast(1), rank_vector[2]); + CHECK_EQUAL(static_cast(4), rank_vector[3]); + CHECK_EQUAL(static_cast(3), rank_vector[4]); +} + +#endif // BOOST_MATH_EXEC_COMPATIBLE + +int main(void) +{ + test(); + test(); + test(); + + container_test(); + container_test(); + container_test(); + + #ifdef BOOST_MATH_EXEC_COMPATIBLE + + execution_test(std::execution::par); + execution_test(std::execution::par); + execution_test(std::execution::par); + + #endif // BOOST_MATH_EXEC_COMPATIBLE + + return boost::math::test::report_errors(); +} From 3fa245da6d3f6229385cfc7f4928dc603e4e19ff Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 26 May 2022 12:11:27 +0100 Subject: [PATCH 41/60] Strip CI back to minimal failing test cases. In the hopes we might actually see some results on Github. --- .github/workflows/ci.yml | 470 +-------------------------------------- 1 file changed, 5 insertions(+), 465 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6d46ae6b5..a4e58372af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,9 +18,9 @@ jobs: strategy: fail-fast: false matrix: - compiler: [ g++-12, clang++-14 ] - standard: [ c++11, c++14, c++17, c++20 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + compiler: [ g++-12 ] + standard: [ c++20 ] + suite: [ special_fun ] steps: - uses: actions/checkout@v2 with: @@ -77,77 +77,13 @@ jobs: - name: Test run: ../../../b2 -j2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test - ubuntu-focal: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - compiler: [ g++-9, g++-11, clang++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Set TOOLSET - run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV - - name: Add repository - continue-on-error: true - id: addrepo - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo - continue-on-error: true - id: retry1 - if: steps.addrepo.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo 2 - continue-on-error: true - id: retry2 - if: steps.retry1.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Install packages - run: sudo apt install g++-9 g++-11 clang-9 clang-10 libgmp-dev libmpfr-dev libfftw3-dev - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: ./bootstrap.sh - working-directory: ../boost-root - - name: Generate headers - run: ./b2 headers - working-directory: ../boost-root - - name: Generate user config - run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' - working-directory: ../boost-root - - name: Config info install - run: ../../../b2 config_info_travis_install toolset=$TOOLSET - working-directory: ../boost-root/libs/config/test - - name: Config info - run: ./config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER - working-directory: ../boost-root/libs/math/test ubuntu-focal-no-eh: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: - compiler: [ g++-9, g++-11, clang++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] + compiler: [ g++-9 ] + standard: [ c++14 ] steps: - uses: actions/checkout@v2 with: @@ -204,399 +140,3 @@ jobs: - name: Test run: ../../../b2 toolset=$TOOLSET no_eh_tests exception-handling=off rtti=off define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test - ubuntu-bionic: - runs-on: ubuntu-18.04 - strategy: - fail-fast: false - matrix: - compiler: [ g++-6, clang++-6.0, g++-7, g++-8, clang++-7, clang++-8 ] - standard: [ c++11, c++14, c++17 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Set TOOLSET - run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV - - name: Add repository - continue-on-error: true - id: addrepo - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo - continue-on-error: true - id: retry1 - if: steps.addrepo.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo 2 - continue-on-error: true - id: retry2 - if: steps.retry1.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Install packages - run: sudo apt install g++-6 g++-7 g++-8 clang-6.0 clang-7 clang-8 libgmp-dev libmpfr-dev libfftw3-dev - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: ./bootstrap.sh - working-directory: ../boost-root - - name: Generate headers - run: ./b2 headers - working-directory: ../boost-root - - name: Generate user config - run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' - working-directory: ../boost-root - - name: Config info install - run: ../../../b2 config_info_travis_install toolset=$TOOLSET - working-directory: ../boost-root/libs/config/test - - name: Config info - run: ./config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER - working-directory: ../boost-root/libs/math/test - macos: - runs-on: macos-latest - strategy: - fail-fast: false - matrix: - toolset: [ clang ] - standard: [ 11, 14, 17, 2a ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[linux];[Linux];[LINUX];[standalone];[STANDALONE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: ./bootstrap.sh - working-directory: ../boost-root - - name: Generate headers - run: ./b2 headers - working-directory: ../boost-root - - name: Config info install - run: ../../../b2 config_info_travis_install toolset=${{ matrix.toolset }} cxxstd=${{ matrix.standard }} - working-directory: ../boost-root/libs/config/test - - name: Config info - run: ./config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ../../../b2 toolset=${{ matrix.toolset }} cxxstd=${{ matrix.standard }} ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER - working-directory: ../boost-root/libs/math/test - windows: - runs-on: windows-2019 - defaults: - run: - shell: cmd - env: - ARGS: toolset=${{ matrix.toolset }} address-model=64 cxxstd=${{ matrix.standard }} - strategy: - fail-fast: false - matrix: - toolset: [ gcc, msvc-14.0, msvc-14.2 ] - standard: [ 11, 14, 17 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: xcopy /s /e /q %GITHUB_WORKSPACE% libs\math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: bootstrap - working-directory: ../boost-root - - name: Generate headers - run: b2 headers - working-directory: ../boost-root - - name: Config info install - run: ..\..\..\b2 config_info_travis_install %ARGS% - working-directory: ../boost-root/libs/config/test - - name: Config info - run: config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ..\..\..\b2 --hash %ARGS% define=CI_SUPPRESS_KNOWN_ISSUES ${{ matrix.suite }} - working-directory: ../boost-root/libs/math/test - MSVC2022: - runs-on: windows-2022 - defaults: - run: - shell: cmd - env: - ARGS: address-model=64 cxxstd=${{ matrix.standard }} - strategy: - fail-fast: false - matrix: - standard: [ 14, 17, 20 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE]' - commit-filter-separator: ';' - fail-fast: true - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: xcopy /s /e /q %GITHUB_WORKSPACE% libs\math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: bootstrap - working-directory: ../boost-root - - name: Generate headers - run: b2 headers - working-directory: ../boost-root - - name: Config info install - run: ..\..\..\b2 config_info_travis_install %ARGS% - working-directory: ../boost-root/libs/config/test - - name: Config info - run: config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ..\..\..\b2 --hash %ARGS% define=CI_SUPPRESS_KNOWN_ISSUES ${{ matrix.suite }} - working-directory: ../boost-root/libs/math/test - cygwin: - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - compiler: [ g++-11 ] - standard: [ c++17 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - env: - TOOLSET: gcc - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE]' - commit-filter-separator: ';' - fail-fast: true - - name: Install Cygwin - run: | - choco install git gcc-core gcc-g++ python39 libgmp-devel libmpfr-devel libfftw3-devel --source cygwin - - name: Checkout main boost - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root' - - name: Update tools/boostdep - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && git submodule update --init tools/boostdep' - - name: Copy files - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && cp -r * ../boost-root/libs/math' - - name: Install deps - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && python tools/boostdep/depinst/depinst.py math' - - name: Bootstrap - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && ./bootstrap.sh' - - name: Generate headers - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && ./b2 headers' - - name: Config info install - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root/libs/config/test && ../../../b2 config_info_travis_install toolset=$TOOLSET' - - name: Config info - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root/libs/config/test && ./config_info_travis' - - name: Test - run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root/libs/math/test && ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER' - standalone-compile-tests-gcc: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - compiler: [ g++-10 ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Add repository - continue-on-error: true - id: addrepo - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo - continue-on-error: true - id: retry1 - if: steps.addrepo.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo 2 - continue-on-error: true - id: retry2 - if: steps.retry1.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Install packages - run: sudo apt install g++-10 libgmp-dev libmpfr-dev libfftw3-dev - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Run CMake - run: cmake -DBUILD_TESTING=1 -DCMAKE_CXX_COMPILER=g++-10 . - working-directory: ../boost-root/libs/math - - name: Run Compile Tests - run: make -j$((`nproc`+1)) - working-directory: ../boost-root/libs/math - standalone-compile-tests-clang: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - compiler: [ clang++-10 ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Add repository - continue-on-error: true - id: addrepo - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo - continue-on-error: true - id: retry1 - if: steps.addrepo.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo 2 - continue-on-error: true - id: retry2 - if: steps.retry1.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Install packages - run: sudo apt install clang-10 libgmp-dev libmpfr-dev libfftw3-dev - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Run CMake - run: cmake -DBUILD_TESTING=1 -DCMAKE_CXX_COMPILER=clang++-10 . - working-directory: ../boost-root/libs/math - - name: Run Compile Tests - run: make -j$((`nproc`+1)) - working-directory: ../boost-root/libs/math - standalone-gcc: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - compiler: [ g++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE]' - commit-filter-separator: ';' - fail-fast: true - - name: Set TOOLSET - run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV - - name: Add repository - continue-on-error: true - id: addrepo - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo - continue-on-error: true - id: retry1 - if: steps.addrepo.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo 2 - continue-on-error: true - id: retry2 - if: steps.retry1.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Install packages - run: sudo apt install g++-10 libgmp-dev libmpfr-dev libfftw3-dev - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: ./bootstrap.sh - working-directory: ../boost-root - - name: Generate headers - run: ./b2 headers - working-directory: ../boost-root - - name: Generate user config - run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' - working-directory: ../boost-root - - name: Config info install - run: ../../../b2 config_info_travis_install toolset=$TOOLSET - working-directory: ../boost-root/libs/config/test - - name: Config info - run: ./config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER define=BOOST_MATH_STANDALONE define=BOOST_MP_STANDALONE - working-directory: ../boost-root/libs/math/test From 355237efeb6d0dfb40b988fdbadfa5fee310e843 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 26 May 2022 12:59:56 +0100 Subject: [PATCH 42/60] Add missing #include to chebeshev.hpp. --- include/boost/math/special_functions/chebyshev.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/math/special_functions/chebyshev.hpp b/include/boost/math/special_functions/chebyshev.hpp index 2ea1560986..7aff765e6f 100644 --- a/include/boost/math/special_functions/chebyshev.hpp +++ b/include/boost/math/special_functions/chebyshev.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #if (__cplusplus > 201103) || (defined(_CPPLIB_VER) && (_CPPLIB_VER >= 610)) # define BOOST_MATH_CHEB_USE_STD_ACOSH From 378b6a2273ac57b072c5ea40aca513c12a615472 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 26 May 2022 13:02:35 +0100 Subject: [PATCH 43/60] Disable gcc-12 C++20 testing as it runs the machine out of memory compiling test_2F0.cpp. --- test/test_2F0.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_2F0.cpp b/test/test_2F0.cpp index 988ed9c0bd..9c951bfdd8 100644 --- a/test/test_2F0.cpp +++ b/test/test_2F0.cpp @@ -97,6 +97,11 @@ BOOST_AUTO_TEST_CASE( test_main ) #endif #endif +#if defined(__GNUC__) && (__GNUC__ == 12) && (__cplusplus > 202000) + // gcc-12 runs the machine out of memory in C++20 mode: +#define BOOST_MATH_NO_MP_TESTS +#endif + #ifndef BOOST_MATH_NO_MP_TESTS using dec_40 = boost::multiprecision::number>; #if !defined(TEST) || (TEST == 3) From 17813330e5544ca4b620a61aae288c0f644cbfa1 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 26 May 2022 16:43:26 +0100 Subject: [PATCH 44/60] Revert "Strip CI back to minimal failing test cases." This reverts commit 3fa245da6d3f6229385cfc7f4928dc603e4e19ff. --- .github/workflows/ci.yml | 470 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 465 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4e58372af..f6d46ae6b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,9 +18,9 @@ jobs: strategy: fail-fast: false matrix: - compiler: [ g++-12 ] - standard: [ c++20 ] - suite: [ special_fun ] + compiler: [ g++-12, clang++-14 ] + standard: [ c++11, c++14, c++17, c++20 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 with: @@ -77,13 +77,77 @@ jobs: - name: Test run: ../../../b2 -j2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test + ubuntu-focal: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + compiler: [ g++-9, g++-11, clang++-10 ] + standard: [ c++11, c++14, c++17, c++2a ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Set TOOLSET + run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install g++-9 g++-11 clang-9 clang-10 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: ./bootstrap.sh + working-directory: ../boost-root + - name: Generate headers + run: ./b2 headers + working-directory: ../boost-root + - name: Generate user config + run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' + working-directory: ../boost-root + - name: Config info install + run: ../../../b2 config_info_travis_install toolset=$TOOLSET + working-directory: ../boost-root/libs/config/test + - name: Config info + run: ./config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER + working-directory: ../boost-root/libs/math/test ubuntu-focal-no-eh: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: - compiler: [ g++-9 ] - standard: [ c++14 ] + compiler: [ g++-9, g++-11, clang++-10 ] + standard: [ c++11, c++14, c++17, c++2a ] steps: - uses: actions/checkout@v2 with: @@ -140,3 +204,399 @@ jobs: - name: Test run: ../../../b2 toolset=$TOOLSET no_eh_tests exception-handling=off rtti=off define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test + ubuntu-bionic: + runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + compiler: [ g++-6, clang++-6.0, g++-7, g++-8, clang++-7, clang++-8 ] + standard: [ c++11, c++14, c++17 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Set TOOLSET + run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install g++-6 g++-7 g++-8 clang-6.0 clang-7 clang-8 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: ./bootstrap.sh + working-directory: ../boost-root + - name: Generate headers + run: ./b2 headers + working-directory: ../boost-root + - name: Generate user config + run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' + working-directory: ../boost-root + - name: Config info install + run: ../../../b2 config_info_travis_install toolset=$TOOLSET + working-directory: ../boost-root/libs/config/test + - name: Config info + run: ./config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER + working-directory: ../boost-root/libs/math/test + macos: + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + toolset: [ clang ] + standard: [ 11, 14, 17, 2a ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[linux];[Linux];[LINUX];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: ./bootstrap.sh + working-directory: ../boost-root + - name: Generate headers + run: ./b2 headers + working-directory: ../boost-root + - name: Config info install + run: ../../../b2 config_info_travis_install toolset=${{ matrix.toolset }} cxxstd=${{ matrix.standard }} + working-directory: ../boost-root/libs/config/test + - name: Config info + run: ./config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ../../../b2 toolset=${{ matrix.toolset }} cxxstd=${{ matrix.standard }} ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER + working-directory: ../boost-root/libs/math/test + windows: + runs-on: windows-2019 + defaults: + run: + shell: cmd + env: + ARGS: toolset=${{ matrix.toolset }} address-model=64 cxxstd=${{ matrix.standard }} + strategy: + fail-fast: false + matrix: + toolset: [ gcc, msvc-14.0, msvc-14.2 ] + standard: [ 11, 14, 17 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: xcopy /s /e /q %GITHUB_WORKSPACE% libs\math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: bootstrap + working-directory: ../boost-root + - name: Generate headers + run: b2 headers + working-directory: ../boost-root + - name: Config info install + run: ..\..\..\b2 config_info_travis_install %ARGS% + working-directory: ../boost-root/libs/config/test + - name: Config info + run: config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ..\..\..\b2 --hash %ARGS% define=CI_SUPPRESS_KNOWN_ISSUES ${{ matrix.suite }} + working-directory: ../boost-root/libs/math/test + MSVC2022: + runs-on: windows-2022 + defaults: + run: + shell: cmd + env: + ARGS: address-model=64 cxxstd=${{ matrix.standard }} + strategy: + fail-fast: false + matrix: + standard: [ 14, 17, 20 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE]' + commit-filter-separator: ';' + fail-fast: true + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: xcopy /s /e /q %GITHUB_WORKSPACE% libs\math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: bootstrap + working-directory: ../boost-root + - name: Generate headers + run: b2 headers + working-directory: ../boost-root + - name: Config info install + run: ..\..\..\b2 config_info_travis_install %ARGS% + working-directory: ../boost-root/libs/config/test + - name: Config info + run: config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ..\..\..\b2 --hash %ARGS% define=CI_SUPPRESS_KNOWN_ISSUES ${{ matrix.suite }} + working-directory: ../boost-root/libs/math/test + cygwin: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + compiler: [ g++-11 ] + standard: [ c++17 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + env: + TOOLSET: gcc + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE]' + commit-filter-separator: ';' + fail-fast: true + - name: Install Cygwin + run: | + choco install git gcc-core gcc-g++ python39 libgmp-devel libmpfr-devel libfftw3-devel --source cygwin + - name: Checkout main boost + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root' + - name: Update tools/boostdep + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && git submodule update --init tools/boostdep' + - name: Copy files + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && cp -r * ../boost-root/libs/math' + - name: Install deps + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && python tools/boostdep/depinst/depinst.py math' + - name: Bootstrap + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && ./bootstrap.sh' + - name: Generate headers + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root && ./b2 headers' + - name: Config info install + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root/libs/config/test && ../../../b2 config_info_travis_install toolset=$TOOLSET' + - name: Config info + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root/libs/config/test && ./config_info_travis' + - name: Test + run: C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE")/../boost-root/libs/math/test && ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER' + standalone-compile-tests-gcc: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + compiler: [ g++-10 ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install g++-10 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Run CMake + run: cmake -DBUILD_TESTING=1 -DCMAKE_CXX_COMPILER=g++-10 . + working-directory: ../boost-root/libs/math + - name: Run Compile Tests + run: make -j$((`nproc`+1)) + working-directory: ../boost-root/libs/math + standalone-compile-tests-clang: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + compiler: [ clang++-10 ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install clang-10 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Run CMake + run: cmake -DBUILD_TESTING=1 -DCMAKE_CXX_COMPILER=clang++-10 . + working-directory: ../boost-root/libs/math + - name: Run Compile Tests + run: make -j$((`nproc`+1)) + working-directory: ../boost-root/libs/math + standalone-gcc: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + compiler: [ g++-10 ] + standard: [ c++11, c++14, c++17, c++2a ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE]' + commit-filter-separator: ';' + fail-fast: true + - name: Set TOOLSET + run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV + - name: Add repository + continue-on-error: true + id: addrepo + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo + continue-on-error: true + id: retry1 + if: steps.addrepo.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Retry Add Repo 2 + continue-on-error: true + id: retry2 + if: steps.retry1.outcome=='failure' + run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + - name: Install packages + run: sudo apt install g++-10 libgmp-dev libmpfr-dev libfftw3-dev + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: cp -r $GITHUB_WORKSPACE/* libs/math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: ./bootstrap.sh + working-directory: ../boost-root + - name: Generate headers + run: ./b2 headers + working-directory: ../boost-root + - name: Generate user config + run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' + working-directory: ../boost-root + - name: Config info install + run: ../../../b2 config_info_travis_install toolset=$TOOLSET + working-directory: ../boost-root/libs/config/test + - name: Config info + run: ./config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER define=BOOST_MATH_STANDALONE define=BOOST_MP_STANDALONE + working-directory: ../boost-root/libs/math/test From 8f808da3a7525c4efbacb733804996cad44fecf0 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 27 May 2022 15:25:44 +0100 Subject: [PATCH 45/60] Disable test_2F0 MP tests for all gcc-12. They run the machine out of memory. --- test/test_2F0.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_2F0.cpp b/test/test_2F0.cpp index 9c951bfdd8..3726a61840 100644 --- a/test/test_2F0.cpp +++ b/test/test_2F0.cpp @@ -97,8 +97,8 @@ BOOST_AUTO_TEST_CASE( test_main ) #endif #endif -#if defined(__GNUC__) && (__GNUC__ == 12) && (__cplusplus > 202000) - // gcc-12 runs the machine out of memory in C++20 mode: +#if defined(__GNUC__) && (__GNUC__ == 12) + // gcc-12 runs the machine out of memory: #define BOOST_MATH_NO_MP_TESTS #endif From 25e51f773ae1967a5d6691fcc87742547b3527ce Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 27 May 2022 18:46:47 +0100 Subject: [PATCH 46/60] Fix condition_numbers.hpp for no-eh usage. Enclose try...catch keywords in BOOST_NO_EXCEPTIONS check. --- include/boost/math/tools/condition_numbers.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/boost/math/tools/condition_numbers.hpp b/include/boost/math/tools/condition_numbers.hpp index a5801996a5..8754d12828 100644 --- a/include/boost/math/tools/condition_numbers.hpp +++ b/include/boost/math/tools/condition_numbers.hpp @@ -94,15 +94,18 @@ Real evaluation_condition_number(F const & f, Real const & x) } bool caught_exception = false; Real fp; +#ifndef BOOST_NO_EXCEPTIONS try { +#endif fp = finite_difference_derivative(f, x); +#ifndef BOOST_NO_EXCEPTIONS } catch(...) { caught_exception = true; } - +#endif if (isnan(fp) || caught_exception) { // Check if the right derivative exists: From 14d6cfdea2e5a82888d281700e9ae787fc4b1c53 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 30 May 2022 18:37:05 +0100 Subject: [PATCH 47/60] Tweak CI runs: Increase tolerance in chatterjee_correlation test. Remove a few tests from Github CI. Remove autodiff from the sanitizer tests as they time out. --- .drone.star | 2 +- .github/workflows/ci.yml | 6 +++--- test/test_chatterjee_correlation.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.drone.star b/.drone.star index fdb0db327d..22a127991b 100644 --- a/.drone.star +++ b/.drone.star @@ -16,7 +16,7 @@ windowsglobalimage="cppalliance/dronevs2019" def main(ctx): things_to_test = [ "special_fun", "distribution_tests", "mp", "misc", "interpolators", "quadrature", "autodiff", "long-running-tests", "float128_tests" ] - sanitizer_test = [ "special_fun", "distribution_tests", "misc", "interpolators", "quadrature", "autodiff", "float128_tests" ] + sanitizer_test = [ "special_fun", "distribution_tests", "misc", "interpolators", "quadrature", "float128_tests" ] gnu_5_stds = [ "gnu++11" ] gnu_6_stds = [ "gnu++11", "gnu++14" ] gnu_8_stds = [ "gnu++11", "gnu++14", "gnu++17" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6d46ae6b5..0e3576aed3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-12, clang++-14 ] - standard: [ c++11, c++14, c++17, c++20 ] + standard: [ c++14, c++17, c++20 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -83,7 +83,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-9, g++-11, clang++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] + standard: [ c++14, c++17, c++2a ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -274,7 +274,7 @@ jobs: fail-fast: false matrix: toolset: [ clang ] - standard: [ 11, 14, 17, 2a ] + standard: [ 11, 14, 17, 20 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 diff --git a/test/test_chatterjee_correlation.cpp b/test/test_chatterjee_correlation.cpp index be91a925e6..d39a1a33a2 100644 --- a/test/test_chatterjee_correlation.cpp +++ b/test/test_chatterjee_correlation.cpp @@ -127,7 +127,7 @@ void test_paper() } result = chatterjee_correlation(x, y); - CHECK_MOLLIFIED_CLOSE(result, Real(0.885), 0.01); + CHECK_MOLLIFIED_CLOSE(result, Real(0.885), 0.012); } int main(void) From 3107abaa8abfc5a91f4dda6bbbf728264035e1e9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 30 May 2022 13:35:54 -0700 Subject: [PATCH 48/60] Constexpr fma (#734) * constexpr fma * Improve use of intrinsics for calculation * Changes to intrinsics and address sonarlint comments --- doc/sf/ccmath.qbk | 7 ++ include/boost/math/ccmath/ccmath.hpp | 1 + include/boost/math/ccmath/fma.hpp | 128 +++++++++++++++++++++ test/Jamfile.v2 | 1 + test/ccmath_fma_test.cpp | 73 ++++++++++++ test/compile_test/ccmath_fma_incl_test.cpp | 16 +++ 6 files changed, 226 insertions(+) create mode 100644 include/boost/math/ccmath/fma.hpp create mode 100644 test/ccmath_fma_test.cpp create mode 100644 test/compile_test/ccmath_fma_incl_test.cpp diff --git a/doc/sf/ccmath.qbk b/doc/sf/ccmath.qbk index 0a88ce6310..4a2b0b2fbf 100644 --- a/doc/sf/ccmath.qbk +++ b/doc/sf/ccmath.qbk @@ -182,6 +182,13 @@ All of the following functions require C++17 or greater. template inline constexpr bool isunordered(T x, T y) noexcept + template + inline constexpr Real fma(Real x, Real y, Real z) noexcept + Requires compiling with fma flag + + template + inline constepxr Promoted fma(Arithmetic1 x, Arithmetic2 y, Arithmetic3 z) noexcept + } // Namespaces [endsect] [/section:ccmath Constexpr CMath] diff --git a/include/boost/math/ccmath/ccmath.hpp b/include/boost/math/ccmath/ccmath.hpp index 72c49922f1..2749ec7b28 100644 --- a/include/boost/math/ccmath/ccmath.hpp +++ b/include/boost/math/ccmath/ccmath.hpp @@ -38,5 +38,6 @@ #include #include #include +#include #endif // BOOST_MATH_CCMATH_HPP diff --git a/include/boost/math/ccmath/fma.hpp b/include/boost/math/ccmath/fma.hpp new file mode 100644 index 0000000000..3056f76d47 --- /dev/null +++ b/include/boost/math/ccmath/fma.hpp @@ -0,0 +1,128 @@ +// (C) Copyright Matt Borland 2022. +// 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) + +#ifndef BOOST_MATH_CCMATH_FMA_HPP +#define BOOST_MATH_CCMATH_FMA_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost::math::ccmath { + +namespace detail { + +template +constexpr T fma_imp(const T x, const T y, const T z) noexcept +{ + #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__INTEL_LLVM_COMPILER) + if constexpr (std::is_same_v) + { + return __builtin_fmaf(x, y, z); + } + else if constexpr (std::is_same_v) + { + return __builtin_fma(x, y, z); + } + else if constexpr (std::is_same_v) + { + return __builtin_fmal(x, y, z); + } + #endif + + // If we can't use compiler intrinsics hope that -fma flag optimizes this call to fma instruction + return (x * y) + z; +} + +} // Namespace detail + +template , bool> = true> +constexpr Real fma(Real x, Real y, Real z) noexcept +{ + if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) + { + if (x == 0 && boost::math::ccmath::isinf(y)) + { + return std::numeric_limits::quiet_NaN(); + } + else if (y == 0 && boost::math::ccmath::isinf(x)) + { + return std::numeric_limits::quiet_NaN(); + } + else if (boost::math::ccmath::isnan(x)) + { + return std::numeric_limits::quiet_NaN(); + } + else if (boost::math::ccmath::isnan(y)) + { + return std::numeric_limits::quiet_NaN(); + } + else if (boost::math::ccmath::isnan(z)) + { + return std::numeric_limits::quiet_NaN(); + } + + return boost::math::ccmath::detail::fma_imp(x, y, z); + } + else + { + using std::fma; + return fma(x, y, z); + } +} + +template +constexpr auto fma(T1 x, T2 y, T3 z) noexcept +{ + if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) + { + // If the type is an integer (e.g. epsilon == 0) then set the epsilon value to 1 so that type is at a minimum + // cast to double + constexpr auto T1p = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; + constexpr auto T2p = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; + constexpr auto T3p = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; + + using promoted_type = + #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + std::conditional_t>>>>>; + #else + >>>; + #endif + + return boost::math::ccmath::fma(promoted_type(x), promoted_type(y), promoted_type(z)); + } + else + { + using std::fma; + return fma(x, y, z); + } +} + +constexpr float fmaf(float x, float y, float z) noexcept +{ + return boost::math::ccmath::fma(x, y, z); +} + +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS +constexpr long double fmal(long double x, long double y, long double z) noexcept +{ + return boost::math::ccmath::fma(x, y, z); +} +#endif + +} // Namespace boost::math::ccmath + +#endif // BOOST_MATH_CCMATH_FMA_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f3df8c2c4f..7a5fb79be3 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -154,6 +154,7 @@ test-suite special_fun : [ run ccmath_isless_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_islessequal_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_isunordered_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] + [ run ccmath_fma_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run log1p_expm1_test.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] [ run powm1_sqrtp1m1_test.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] [ run git_issue_705.cpp ../../test/build//boost_unit_test_framework ] diff --git a/test/ccmath_fma_test.cpp b/test/ccmath_fma_test.cpp new file mode 100644 index 0000000000..a5aa74914d --- /dev/null +++ b/test/ccmath_fma_test.cpp @@ -0,0 +1,73 @@ +// (C) Copyright Matt Borland 2022. +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_FLOAT128 +#include +#endif + +#if !defined(BOOST_MATH_NO_CONSTEXPR_DETECTION) && !defined(BOOST_MATH_USING_BUILTIN_CONSTANT_P) +template +constexpr void test() +{ + // Error handling + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(std::numeric_limits::infinity(), T(0), T(1)))); + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(T(0), std::numeric_limits::infinity(), T(1)))); + + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(std::numeric_limits::infinity(), T(0), std::numeric_limits::quiet_NaN()))); + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(T(0), std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()))); + + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(std::numeric_limits::quiet_NaN(), T(1), T(1)))); + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(T(1), std::numeric_limits::quiet_NaN(), T(1)))); + + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::fma(T(1), T(1), std::numeric_limits::quiet_NaN()))); + + // Functionality + static_assert(boost::math::ccmath::fma(T(1), T(2), T(3)) == T(5)); + static_assert(boost::math::ccmath::fma(T(2), T(3), T(1)) == T(7)); + + // Correct promoted types + if constexpr (!std::is_same_v) + { + constexpr auto test_type = boost::math::ccmath::fma(T(1), 1.0, 1.0f); + static_assert(std::is_same_v>); + } + else + { + constexpr auto test_type = boost::math::ccmath::fma(1.0f, 1, 1.0); + static_assert(std::is_same_v>); + } +} + +int main() +{ + test(); + test(); + + #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + test(); + #endif + + #ifdef BOOST_HAS_FLOAT128 + test(); + #endif + + return 0; +} +#else +int main() +{ + return 0; +} +#endif diff --git a/test/compile_test/ccmath_fma_incl_test.cpp b/test/compile_test/ccmath_fma_incl_test.cpp new file mode 100644 index 0000000000..ef035bd8b6 --- /dev/null +++ b/test/compile_test/ccmath_fma_incl_test.cpp @@ -0,0 +1,16 @@ +// (C) Copyright Matt Borland 2022. +// 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 +#include "test_compile_result.hpp" + +void compile_and_link_test() +{ + check_result(boost::math::ccmath::fma(1.0f, 1.0f, 1.0f)); + check_result(boost::math::ccmath::fma(1.0, 1.0, 1.0)); +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + check_result(boost::math::ccmath::fma(1.0l, 1.0l, 1.0l)); +#endif +} From 8b7e1540448054474127e387af1ce83ebd40e590 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Tue, 31 May 2022 18:16:57 +0100 Subject: [PATCH 49/60] Move all GCC-5/6 testing to drone. Remove C++11 testing for gcc where the compiler default is C++14. Remove C++11 testing from msvc - it doesn't support it, only C++14. All C++11 testing is now on gcc-5 and 6 and on drone. --- .drone.star | 4 ++-- .github/workflows/ci.yml | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.drone.star b/.drone.star index 22a127991b..1dbd504ed6 100644 --- a/.drone.star +++ b/.drone.star @@ -17,8 +17,8 @@ def main(ctx): things_to_test = [ "special_fun", "distribution_tests", "mp", "misc", "interpolators", "quadrature", "autodiff", "long-running-tests", "float128_tests" ] sanitizer_test = [ "special_fun", "distribution_tests", "misc", "interpolators", "quadrature", "float128_tests" ] - gnu_5_stds = [ "gnu++11" ] - gnu_6_stds = [ "gnu++11", "gnu++14" ] + gnu_5_stds = [ "gnu++11", "c++11" ] + gnu_6_stds = [ "gnu++11", "gnu++14", "c++11", "c++14" ] gnu_8_stds = [ "gnu++11", "gnu++14", "gnu++17" ] gnu_10_stds = [ "gnu++11", "gnu++14", "gnu++17", "gnu++20" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e3576aed3..608760207a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,7 +147,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-9, g++-11, clang++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] + standard: [ c++14, c++17, c++2a ] steps: - uses: actions/checkout@v2 with: @@ -209,8 +209,8 @@ jobs: strategy: fail-fast: false matrix: - compiler: [ g++-6, clang++-6.0, g++-7, g++-8, clang++-7, clang++-8 ] - standard: [ c++11, c++14, c++17 ] + compiler: [ clang++-6.0, g++-7, g++-8, clang++-7, clang++-8 ] + standard: [ c++14, c++17 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -274,7 +274,7 @@ jobs: fail-fast: false matrix: toolset: [ clang ] - standard: [ 11, 14, 17, 20 ] + standard: [ 14, 17, 20 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -322,7 +322,7 @@ jobs: fail-fast: false matrix: toolset: [ gcc, msvc-14.0, msvc-14.2 ] - standard: [ 11, 14, 17 ] + standard: [ 14, 17 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -412,7 +412,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-11 ] - standard: [ c++17 ] + standard: [ c++14, c++17 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] env: TOOLSET: gcc @@ -542,7 +542,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] + standard: [ c++14, c++17, c++2a ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 From 5fa7797ec6b2d709da2bacee029fcc23332726b7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 31 May 2022 20:19:22 -0700 Subject: [PATCH 50/60] Bump minimum language standard to C++14 (#788) * Bump minimum language standard to C++14 * Add warning message in config * Update readme and keep C++14 in GCC5 drone run --- .drone.star | 2 +- .github/workflows/ci.yml | 6 +++--- README.md | 4 ++-- include/boost/math/tools/config.hpp | 11 +++++++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.drone.star b/.drone.star index 22a127991b..7a1e321a4c 100644 --- a/.drone.star +++ b/.drone.star @@ -17,7 +17,7 @@ def main(ctx): things_to_test = [ "special_fun", "distribution_tests", "mp", "misc", "interpolators", "quadrature", "autodiff", "long-running-tests", "float128_tests" ] sanitizer_test = [ "special_fun", "distribution_tests", "misc", "interpolators", "quadrature", "float128_tests" ] - gnu_5_stds = [ "gnu++11" ] + gnu_5_stds = [ "gnu++11", "gnu++14" ] gnu_6_stds = [ "gnu++11", "gnu++14" ] gnu_8_stds = [ "gnu++11", "gnu++14", "gnu++17" ] gnu_10_stds = [ "gnu++11", "gnu++14", "gnu++17", "gnu++20" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e3576aed3..e582876fb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -210,7 +210,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-6, clang++-6.0, g++-7, g++-8, clang++-7, clang++-8 ] - standard: [ c++11, c++14, c++17 ] + standard: [ c++14, c++17 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -322,7 +322,7 @@ jobs: fail-fast: false matrix: toolset: [ gcc, msvc-14.0, msvc-14.2 ] - standard: [ 11, 14, 17 ] + standard: [ 14, 17 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 @@ -542,7 +542,7 @@ jobs: fail-fast: false matrix: compiler: [ g++-10 ] - standard: [ c++11, c++14, c++17, c++2a ] + standard: [ c++14, c++17, c++20 ] suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, interpolators, autodiff, ../example//examples, ../tools ] steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index d6f6b920f6..b9dba9a9a9 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ Boost Math Library [![Build Status](https://drone.cpp.al/api/badges/boostorg/math/status.svg)](https://drone.cpp.al/boostorg/math)[![Build Status](https://github.com/boostorg/math/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/math/actions) ================== ->ANNOUNCEMENT: Support for C++03 is now deprecated in this library and will be supported in existing features ->only until March 2021. New features will require *at least* C++11, as will existing features from next year. +>ANNOUNCEMENT: Support for C++11 will be deprecated in this library starting in July 2023 (Boost 1.82). +>New features will require *at least* C++14, as will existing features starting with the deprecation release. This library is divided into several interconnected parts: diff --git a/include/boost/math/tools/config.hpp b/include/boost/math/tools/config.hpp index 6eeb0b9521..09db298a88 100644 --- a/include/boost/math/tools/config.hpp +++ b/include/boost/math/tools/config.hpp @@ -13,6 +13,17 @@ #include +// Minimum language standard transition +#ifdef _MSVC_LANG +# if _MSVC_LANG < 201402L +# pragma warning("The minimum language standard to use Boost.Math will be C++14 starting in July 2023 (Boost 1.82 release)"); +# endif +#else +# if __cplusplus < 201402L +# warning "The minimum language standard to use Boost.Math will be C++14 starting in July 2023 (Boost 1.82 release)" +# endif +#endif + #ifndef BOOST_MATH_STANDALONE #include From 3517400cd9a5d276f31bf6de8481a1a2a2cc79df Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 2 Jun 2022 18:30:14 +0100 Subject: [PATCH 51/60] Correct drone script. --- .drone.star | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.star b/.drone.star index c0eb84bc51..8bef21a84b 100644 --- a/.drone.star +++ b/.drone.star @@ -38,13 +38,13 @@ def main(ctx): result.append(linux_cxx("Ubunti g++-5 " + cxx + " " + suite, "g++-5", packages="g++-5", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-5', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in gnu_6_stds: result.append(linux_cxx("Ubunti g++-6 " + cxx + " " + suite, "g++-6", packages="g++-6", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-6', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) - result.append(linux_cxx("Ubunti g++-7 " + cxx + " " + suite, "g++-7", packages="g++-7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-6', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti g++-7 " + cxx + " " + suite, "g++-7", packages="g++-7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-7', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-8 " + cxx + " " + suite, "g++-8", packages="g++-8", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-9 " + cxx + " " + suite, "g++-9", packages="g++-9", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in clang_6_stds: - result.append(linux_cxx("Ubunti clang++-6 " + cxx + " " + suite, "clang++-6", packages="clang-6", llvm_os="xenial", llvm_ver="6", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) - result.append(linux_cxx("Ubunti clang++-7 " + cxx + " " + suite, "clang++-7", packages="clang-7", llvm_os="xenial", llvm_ver="7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) - result.append(linux_cxx("Ubunti clang++-8 " + cxx + " " + suite, "clang++-8", packages="clang-8", llvm_os="xenial", llvm_ver="8", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti clang++-6 " + cxx + " " + suite, "clang++-6", packages="clang-6", llvm_os="xenial", llvm_ver="6", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-6', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti clang++-7 " + cxx + " " + suite, "clang++-7", packages="clang-7", llvm_os="xenial", llvm_ver="7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-7', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti clang++-8 " + cxx + " " + suite, "clang++-8", packages="clang-8", llvm_os="xenial", llvm_ver="8", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-9 " + cxx + " " + suite, "clang++-9", packages="clang-9", llvm_os="xenial", llvm_ver="9", buildtype="boost", image="cppalliance/droneubuntu1904:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in gnu_10_stds: result.append(linux_cxx("Ubunti g++-10 " + cxx + " " + suite, "g++-10", packages="g++-10", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-10', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) From 71e9d5c3cd9705cd767e2e89907ac8789b92ff5c Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 2 Jun 2022 18:36:45 +0100 Subject: [PATCH 52/60] Move Ubuntu-focal tests to drone. --- .drone.star | 8 +++-- .github/workflows/ci.yml | 64 ---------------------------------------- 2 files changed, 6 insertions(+), 66 deletions(-) diff --git a/.drone.star b/.drone.star index 8bef21a84b..4f345d701f 100644 --- a/.drone.star +++ b/.drone.star @@ -20,7 +20,8 @@ def main(ctx): gnu_5_stds = [ "gnu++11", "c++11", "gnu++14", "c++14" ] gnu_6_stds = [ "gnu++11", "c++11", "gnu++14", "c++14", "gnu++17", "c++17" ] clang_6_stds = [ "c++11", "c++14", "c++17" ] - gnu_10_stds = [ "gnu++11", "gnu++14", "gnu++17", "gnu++20" ] + gnu_9_stds = [ "gnu++14", "c++14", "gnu++17", "c++17", "gnu++2a", "c++2a" ] + clang_10_stds = [ "c++14", "c++17", "c++2a" ] result = [] @@ -46,8 +47,11 @@ def main(ctx): result.append(linux_cxx("Ubunti clang++-7 " + cxx + " " + suite, "clang++-7", packages="clang-7", llvm_os="xenial", llvm_ver="7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-7', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-8 " + cxx + " " + suite, "clang++-8", packages="clang-8", llvm_os="xenial", llvm_ver="8", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-9 " + cxx + " " + suite, "clang++-9", packages="clang-9", llvm_os="xenial", llvm_ver="9", buildtype="boost", image="cppalliance/droneubuntu1904:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) - for cxx in gnu_10_stds: + for cxx in gnu_9_stds: + result.append(linux_cxx("Ubunti g++-9 " + cxx + " " + suite, "g++-9", packages="g++-9", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-10 " + cxx + " " + suite, "g++-10", packages="g++-10", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-10', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti g++-11 " + cxx + " " + suite, "g++-11", packages="g++-11", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-11', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + for cxx in clang_10_stds: result.append(linux_cxx("Ubunti clang++-10 " + cxx + " " + suite, "clang++-10", packages="clang-10", llvm_os="xenial", llvm_ver="10", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-10', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) return result diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 986d8e5504..8f85be64d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,70 +77,6 @@ jobs: - name: Test run: ../../../b2 -j2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER working-directory: ../boost-root/libs/math/test - ubuntu-focal: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - compiler: [ g++-9, g++-11, clang++-10 ] - standard: [ c++14, c++17, c++2a ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - uses: mstachniuk/ci-skip@v1 - with: - commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[windows];[Windows];[WINDOWS];[apple];[Apple];[APPLE];[standalone];[STANDALONE];[cygwin];[CYGWIN]' - commit-filter-separator: ';' - fail-fast: true - - name: Set TOOLSET - run: echo ${{ matrix.compiler }} | awk '/^g/ { print "TOOLSET=gcc" } /^clang/ { print "TOOLSET=clang" }' >> $GITHUB_ENV - - name: Add repository - continue-on-error: true - id: addrepo - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo - continue-on-error: true - id: retry1 - if: steps.addrepo.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Retry Add Repo 2 - continue-on-error: true - id: retry2 - if: steps.retry1.outcome=='failure' - run: sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - - name: Install packages - run: sudo apt install g++-9 g++-11 clang-9 clang-10 libgmp-dev libmpfr-dev libfftw3-dev - - name: Checkout main boost - run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root - - name: Update tools/boostdep - run: git submodule update --init tools/boostdep - working-directory: ../boost-root - - name: Copy files - run: cp -r $GITHUB_WORKSPACE/* libs/math - working-directory: ../boost-root - - name: Install deps - run: python tools/boostdep/depinst/depinst.py math - working-directory: ../boost-root - - name: Bootstrap - run: ./bootstrap.sh - working-directory: ../boost-root - - name: Generate headers - run: ./b2 headers - working-directory: ../boost-root - - name: Generate user config - run: 'echo "using $TOOLSET : : ${{ matrix.compiler }} : -std=${{ matrix.standard }} ;" > ~/user-config.jam' - working-directory: ../boost-root - - name: Config info install - run: ../../../b2 config_info_travis_install toolset=$TOOLSET - working-directory: ../boost-root/libs/config/test - - name: Config info - run: ./config_info_travis - working-directory: ../boost-root/libs/config/test - - name: Test - run: ../../../b2 toolset=$TOOLSET ${{ matrix.suite }} define=CI_SUPPRESS_KNOWN_ISSUES define=SLOW_COMPILER - working-directory: ../boost-root/libs/math/test ubuntu-focal-no-eh: runs-on: ubuntu-20.04 strategy: From 922533913188c0d30cf99cc78e94f0399697ec51 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 2 Jun 2022 18:49:59 +0100 Subject: [PATCH 53/60] Remove duplicate pipeline name in drone config. --- .drone.star | 1 - 1 file changed, 1 deletion(-) diff --git a/.drone.star b/.drone.star index 4f345d701f..fdc43bc824 100644 --- a/.drone.star +++ b/.drone.star @@ -48,7 +48,6 @@ def main(ctx): result.append(linux_cxx("Ubunti clang++-8 " + cxx + " " + suite, "clang++-8", packages="clang-8", llvm_os="xenial", llvm_ver="8", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-9 " + cxx + " " + suite, "clang++-9", packages="clang-9", llvm_os="xenial", llvm_ver="9", buildtype="boost", image="cppalliance/droneubuntu1904:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in gnu_9_stds: - result.append(linux_cxx("Ubunti g++-9 " + cxx + " " + suite, "g++-9", packages="g++-9", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-10 " + cxx + " " + suite, "g++-10", packages="g++-10", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-10', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-11 " + cxx + " " + suite, "g++-11", packages="g++-11", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-11', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in clang_10_stds: From c02046fab537de6e60e2e54a6580363ba5acdc1b Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 3 Jun 2022 09:20:27 +0100 Subject: [PATCH 54/60] Attempt to correct drone config. --- .drone.star | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.star b/.drone.star index fdc43bc824..799e729627 100644 --- a/.drone.star +++ b/.drone.star @@ -43,10 +43,10 @@ def main(ctx): result.append(linux_cxx("Ubunti g++-8 " + cxx + " " + suite, "g++-8", packages="g++-8", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-9 " + cxx + " " + suite, "g++-9", packages="g++-9", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in clang_6_stds: - result.append(linux_cxx("Ubunti clang++-6 " + cxx + " " + suite, "clang++-6", packages="clang-6", llvm_os="xenial", llvm_ver="6", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-6', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti clang++-6 " + cxx + " " + suite, "clang++-6", packages="clang-6.0", llvm_os="xenial", llvm_ver="6.0", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-6', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-7 " + cxx + " " + suite, "clang++-7", packages="clang-7", llvm_os="xenial", llvm_ver="7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-7', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-8 " + cxx + " " + suite, "clang++-8", packages="clang-8", llvm_os="xenial", llvm_ver="8", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) - result.append(linux_cxx("Ubunti clang++-9 " + cxx + " " + suite, "clang++-9", packages="clang-9", llvm_os="xenial", llvm_ver="9", buildtype="boost", image="cppalliance/droneubuntu1904:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti clang++-9 " + cxx + " " + suite, "clang++-9", packages="clang-9", llvm_os="xenial", llvm_ver="9", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in gnu_9_stds: result.append(linux_cxx("Ubunti g++-10 " + cxx + " " + suite, "g++-10", packages="g++-10", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-10', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-11 " + cxx + " " + suite, "g++-11", packages="g++-11", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-11', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) From 6f71893098e30f01b4148147df20baa97333fe68 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 3 Jun 2022 18:03:17 +0100 Subject: [PATCH 55/60] Correct clang-6 invocation name in drone. --- .drone.star | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.star b/.drone.star index 799e729627..089e13a959 100644 --- a/.drone.star +++ b/.drone.star @@ -43,7 +43,7 @@ def main(ctx): result.append(linux_cxx("Ubunti g++-8 " + cxx + " " + suite, "g++-8", packages="g++-8", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti g++-9 " + cxx + " " + suite, "g++-9", packages="g++-9", buildtype="boost", image="cppalliance/droneubuntu2004:1", environment={'TOOLSET': 'gcc', 'COMPILER': 'g++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) for cxx in clang_6_stds: - result.append(linux_cxx("Ubunti clang++-6 " + cxx + " " + suite, "clang++-6", packages="clang-6.0", llvm_os="xenial", llvm_ver="6.0", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-6', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) + result.append(linux_cxx("Ubunti clang++-6 " + cxx + " " + suite, "clang++-6.0", packages="clang-6.0", llvm_os="xenial", llvm_ver="6.0", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-6.0', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-7 " + cxx + " " + suite, "clang++-7", packages="clang-7", llvm_os="xenial", llvm_ver="7", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-7', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-8 " + cxx + " " + suite, "clang++-8", packages="clang-8", llvm_os="xenial", llvm_ver="8", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) result.append(linux_cxx("Ubunti clang++-9 " + cxx + " " + suite, "clang++-9", packages="clang-9", llvm_os="xenial", llvm_ver="9", buildtype="boost", image="cppalliance/droneubuntu1804:1", environment={'TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'CXXSTD': cxx, 'TEST_SUITE': suite, }, globalenv=globalenv)) From c5c01b675c7c1f874e1169329f7640ac12bad2fb Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sun, 5 Jun 2022 19:43:43 +0100 Subject: [PATCH 56/60] Try and consolidate more Github tests. In the hopes of speeding up CI build times. --- .github/workflows/ci.yml | 12 ++++++------ test/Jamfile.v2 | 9 ++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f85be64d2..0b1ad2f4aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: matrix: compiler: [ g++-12, clang++-14 ] standard: [ c++14, c++17, c++20 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + suite: [ github_ci_block_1, github_ci_block_2 ] steps: - uses: actions/checkout@v2 with: @@ -147,7 +147,7 @@ jobs: matrix: toolset: [ clang ] standard: [ 11, 14, 17, 20 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + suite: [ github_ci_block_1, github_ci_block_2 ] steps: - uses: actions/checkout@v2 with: @@ -195,7 +195,7 @@ jobs: matrix: toolset: [ gcc, msvc-14.0, msvc-14.2 ] standard: [ 14, 17 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + suite: [ github_ci_block_1, github_ci_block_2 ] steps: - uses: actions/checkout@v2 with: @@ -242,7 +242,7 @@ jobs: fail-fast: false matrix: standard: [ 14, 17, 20 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + suite: [ github_ci_block_1, github_ci_block_2 ] steps: - uses: actions/checkout@v2 with: @@ -285,7 +285,7 @@ jobs: matrix: compiler: [ g++-11 ] standard: [ c++17 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + suite: [ github_ci_block_1, github_ci_block_2 ] env: TOOLSET: gcc steps: @@ -415,7 +415,7 @@ jobs: matrix: compiler: [ g++-10 ] standard: [ c++14, c++17, c++20 ] - suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, interpolators, autodiff, ../example//examples, ../tools ] + suite: [ github_ci_block_1, github_ci_block_2 ] steps: - uses: actions/checkout@v2 with: diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e4ab283cd7..2708b28096 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1633,4 +1633,11 @@ alias no_eh_tests : tools_workaround_incl_test ; -explicit no_eh_tests ; \ No newline at end of file +explicit no_eh_tests ; + +# Some aliases which group blocks of tests for CI testing: + +alias github_ci_block_1 : special_fun float128_tests distribution_tests mp misc ; +alias github_ci_block_2 : quadrature interpolators autodiff ../example//examples ../tools ; +explicit github_ci_block_1 ; +explicit github_ci_block_2 ; From ce370f862747388d818029c34317cb2a45abfbef Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 6 Jun 2022 09:07:36 +0100 Subject: [PATCH 57/60] Fix up test_polynomial.cpp for standalone test. --- test/test_polynomial.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/test_polynomial.cpp b/test/test_polynomial.cpp index e13ceb6196..41a3a511de 100644 --- a/test/test_polynomial.cpp +++ b/test/test_polynomial.cpp @@ -7,13 +7,17 @@ #define BOOST_TEST_MAIN #include #include +#ifndef BOOST_MATH_STANDALONE #include +#endif #include #include #include +#ifndef BOOST_MATH_STANDALONE #include #include #include +#endif #include #include #include @@ -271,7 +275,7 @@ typedef boost::mpl::list large_integral_test_types; typedef boost::mpl::list<> mp_integral_test_types; #elif defined(TEST2) typedef boost::mpl::list< -#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1500) +#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1500) && !defined(BOOST_MATH_STANDALONE) boost::multiprecision::cpp_int #endif > integral_test_types; @@ -287,13 +291,13 @@ typedef large_integral_test_types mp_integral_test_types; typedef boost::mpl::list non_integral_test_types; #elif defined(TEST2) typedef boost::mpl::list< -#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1500) +#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1500) && !defined(BOOST_MATH_STANDALONE) boost::multiprecision::cpp_rational #endif > non_integral_test_types; #elif defined(TEST3) typedef boost::mpl::list< -#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1500) +#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1500) && !defined(BOOST_MATH_STANDALONE) boost::multiprecision::cpp_bin_float_single, boost::multiprecision::cpp_dec_float_50 #endif > non_integral_test_types; From d4c71ea9db462c4df0eb86cacfcdde48c4cde83b Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 6 Jun 2022 12:04:40 +0100 Subject: [PATCH 58/60] Fix up multiprc_concept_check_2.cpp for standalone tests. --- test/multiprc_concept_check_2.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/multiprc_concept_check_2.cpp b/test/multiprc_concept_check_2.cpp index 500e1fc4f3..30311fda24 100644 --- a/test/multiprc_concept_check_2.cpp +++ b/test/multiprc_concept_check_2.cpp @@ -35,7 +35,8 @@ void foo() int main() { - BOOST_CONCEPT_ASSERT((boost::math::concepts::RealTypeConcept)); + boost::math::concepts::RealTypeConcept checker; + checker.constraints(); } From 48f20e9f82cb5fca6c050f0b4426f1a483ceeaa5 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 6 Jun 2022 18:44:24 +0100 Subject: [PATCH 59/60] Split windows-gcc jobs into smaller chunks again. These are much much slower than anything else. --- .github/workflows/ci.yml | 52 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b1ad2f4aa..612fcebbd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -193,7 +193,7 @@ jobs: strategy: fail-fast: false matrix: - toolset: [ gcc, msvc-14.0, msvc-14.2 ] + toolset: [ msvc-14.0, msvc-14.2 ] standard: [ 14, 17 ] suite: [ github_ci_block_1, github_ci_block_2 ] steps: @@ -231,6 +231,54 @@ jobs: - name: Test run: ..\..\..\b2 --hash %ARGS% define=CI_SUPPRESS_KNOWN_ISSUES ${{ matrix.suite }} working-directory: ../boost-root/libs/math/test + windows_gcc: + runs-on: windows-2019 + defaults: + run: + shell: cmd + env: + ARGS: toolset=${{ matrix.toolset }} address-model=64 cxxstd=${{ matrix.standard }} + strategy: + fail-fast: false + matrix: + toolset: [ gcc ] + standard: [ 14, 17 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + - uses: mstachniuk/ci-skip@v1 + with: + commit-filter: '[skip ci];[ci skip];[CI SKIP];[SKIP CI];***CI SKIP***;***SKIP CI***;[apple];[Apple];[APPLE];[linux];[Linux];[LINUX];[standalone];[STANDALONE];[cygwin];[CYGWIN]' + commit-filter-separator: ';' + fail-fast: true + - name: Checkout main boost + run: git clone -b develop --depth 1 https://github.com/boostorg/boost.git ../boost-root + - name: Update tools/boostdep + run: git submodule update --init tools/boostdep + working-directory: ../boost-root + - name: Copy files + run: xcopy /s /e /q %GITHUB_WORKSPACE% libs\math + working-directory: ../boost-root + - name: Install deps + run: python tools/boostdep/depinst/depinst.py math + working-directory: ../boost-root + - name: Bootstrap + run: bootstrap + working-directory: ../boost-root + - name: Generate headers + run: b2 headers + working-directory: ../boost-root + - name: Config info install + run: ..\..\..\b2 config_info_travis_install %ARGS% + working-directory: ../boost-root/libs/config/test + - name: Config info + run: config_info_travis + working-directory: ../boost-root/libs/config/test + - name: Test + run: ..\..\..\b2 --hash %ARGS% define=CI_SUPPRESS_KNOWN_ISSUES ${{ matrix.suite }} + working-directory: ../boost-root/libs/math/test MSVC2022: runs-on: windows-2022 defaults: @@ -285,7 +333,7 @@ jobs: matrix: compiler: [ g++-11 ] standard: [ c++17 ] - suite: [ github_ci_block_1, github_ci_block_2 ] + suite: [ float128_tests, special_fun, distribution_tests, misc, quadrature, mp, interpolators, autodiff, ../example//examples, ../tools ] env: TOOLSET: gcc steps: From b3cec0c732b8f4583157ebf389c85f14dd371021 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 14 Jun 2022 17:55:04 +0100 Subject: [PATCH 60/60] Replace non-UTF-8 copyright character --- example/dot_net_example/distribution_explorer/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/dot_net_example/distribution_explorer/readme.txt b/example/dot_net_example/distribution_explorer/readme.txt index d483173d73..04fec50f21 100644 --- a/example/dot_net_example/distribution_explorer/readme.txt +++ b/example/dot_net_example/distribution_explorer/readme.txt @@ -3,7 +3,7 @@ Statistical Distribution Explorer Paul A. Bristow John Maddock -Copyright © 2008 , 2009, 2010, 2012 Paul A. Bristow, John Maddock +Copyright (C) 2008, 2009, 2010, 2012 Paul A. Bristow, John Maddock Distributed under 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)

-Function Index

+Function Index