From 5e32630a306f8b646c8e6a79a9c1a330c5f33766 Mon Sep 17 00:00:00 2001 From: Jacob Hass Date: Tue, 10 Feb 2026 13:05:12 -0800 Subject: [PATCH 1/5] Implemented keyword for ibeta_imp --- include/boost/math/special_functions/beta.hpp | 98 +++++++++++++++++-- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/include/boost/math/special_functions/beta.hpp b/include/boost/math/special_functions/beta.hpp index f6327fa220..6a2d3c1193 100644 --- a/include/boost/math/special_functions/beta.hpp +++ b/include/boost/math/special_functions/beta.hpp @@ -1195,7 +1195,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_large_ab(T a, T b, T x, T y, bool invert, bool no // template -BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, bool normalised, T* p_derivative) +BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, bool normalised, T* p_derivative, bool logarithm=false) { constexpr auto function = "boost::math::ibeta<%1%>(%1%, %1%, %1%)"; typedef typename lanczos::lanczos::type lanczos_type; @@ -1235,12 +1235,32 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b if(b == 0) return policies::raise_domain_error(function, "The arguments a and b to the incomplete beta function cannot both be zero, with x=%1%.", x, pol); if(b > 0) - return static_cast(inv ? 0 : 1); + { + if (!logarithm) + { + return static_cast(inv ? 0 : 1); + } + else + { + return inv ? -std::numeric_limits::infinity() : T(0); + } + + } + } else if(b == 0) { if(a > 0) - return static_cast(inv ? 1 : 0); + { + if (!logarithm) + { + return static_cast(inv ? 1 : 0); + } + else + { + return inv ? T(0) : -std::numeric_limits::infinity(); + } + } } } else @@ -1257,7 +1277,14 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b { *p_derivative = (a == 1) ? (T)1 : (a < 1) ? T(tools::max_value() / 2) : T(tools::min_value() * 2); } - return (invert ? (normalised ? T(1) : boost::math::beta(a, b, pol)) : T(0)); + if (!logarithm) + { + return (invert ? (normalised ? T(1) : boost::math::beta(a, b, pol)) : T(0)); + } + else + { + return (invert ? (normalised ? 0 : log(boost::math::beta(a, b, pol))) : -std::numeric_limits::infinity()); + } } if(x == 1) { @@ -1265,7 +1292,15 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b { *p_derivative = (b == 1) ? T(1) : (b < 1) ? T(tools::max_value() / 2) : T(tools::min_value() * 2); } - return (invert == 0 ? (normalised ? 1 : boost::math::beta(a, b, pol)) : 0); + if (!logarithm) + { + return (invert == 0 ? (normalised ? 1 : boost::math::beta(a, b, pol)) : 0); + } + else + { + return (invert == 0 ? (normalised ? 0 : log(boost::math::beta(a, b, pol))) : -std::numeric_limits::infinity()); + } + } if((a == 0.5f) && (b == 0.5f)) { @@ -1277,7 +1312,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b T p = invert ? asin(sqrt(y)) / constants::half_pi() : asin(sqrt(x)) / constants::half_pi(); if(!normalised) p *= constants::pi(); - return p; + return logarithm ? log(p) : p; } if(a == 1) { @@ -1294,7 +1329,15 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b { if(p_derivative) *p_derivative = 1; - return invert ? y : x; + if (!logarithm) + { + return invert ? y : x; + } + else + { + return invert ? log(y) : log(x); + } + } if(p_derivative) @@ -1308,7 +1351,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b p = invert ? T(-boost::math::powm1(x, a, pol)) : T(pow(x, a)); if(!normalised) p /= a; - return p; + return logarithm ? log(p) : p; } if(BOOST_MATH_GPU_SAFE_MIN(a, b) <= 1) @@ -1625,7 +1668,15 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b } } } - return invert ? (normalised ? 1 : boost::math::beta(a, b, pol)) - fract : fract; + if (!logarithm) + { + return invert ? (normalised ? 1 : boost::math::beta(a, b, pol)) - fract : fract; + } + else + { + return invert ? (normalised ? std::log1p(-fract) : log(boost::math::beta(a, b, pol) - fract)) : log(fract); + } + } // template T ibeta_imp(T a, T b, T x, const Lanczos& l, bool inv, bool normalised) template @@ -1634,6 +1685,12 @@ BOOST_MATH_GPU_ENABLED inline T ibeta_imp(T a, T b, T x, const Policy& pol, bool return ibeta_imp(a, b, x, pol, inv, normalised, static_cast(nullptr)); } +template +BOOST_MATH_GPU_ENABLED inline T log_ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, bool normalised) +{ + return ibeta_imp(a, b, x, pol, inv, normalised, static_cast(nullptr), true); +} + template BOOST_MATH_GPU_ENABLED T ibeta_derivative_imp(T a, T b, T x, const Policy& pol) { @@ -1794,6 +1851,29 @@ BOOST_MATH_GPU_ENABLED inline typename tools::promote_args::type return boost::math::ibeta(a, b, x, policies::policy<>()); } +template +BOOST_MATH_GPU_ENABLED inline typename tools::promote_args::type + log_ibeta(RT1 a, RT2 b, RT3 x, const Policy&) +{ + BOOST_FPU_EXCEPTION_GUARD + typedef typename tools::promote_args::type result_type; + typedef typename policies::evaluation::type value_type; + typedef typename policies::normalise< + Policy, + policies::promote_float, + policies::promote_double, + policies::discrete_quantile<>, + policies::assert_undefined<> >::type forwarding_policy; + + return policies::checked_narrowing_cast(detail::log_ibeta_imp(static_cast(a), static_cast(b), static_cast(x), forwarding_policy(), false, true), "boost::math::ibeta<%1%>(%1%,%1%,%1%)"); +} +template +BOOST_MATH_GPU_ENABLED inline typename tools::promote_args::type + log_ibeta(RT1 a, RT2 b, RT3 x) +{ + return boost::math::log_ibeta(a, b, x, policies::policy<>()); +} + template BOOST_MATH_GPU_ENABLED inline typename tools::promote_args::type ibetac(RT1 a, RT2 b, RT3 x, const Policy&) From d05919a6ba23a8cdbcb988c9f3968a304332a9b2 Mon Sep 17 00:00:00 2001 From: Jacob Hass Date: Tue, 10 Feb 2026 18:24:23 -0800 Subject: [PATCH 2/5] Fixed bug with type real_concept --- include/boost/math/special_functions/beta.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/math/special_functions/beta.hpp b/include/boost/math/special_functions/beta.hpp index 6a2d3c1193..9e1e82e9a5 100644 --- a/include/boost/math/special_functions/beta.hpp +++ b/include/boost/math/special_functions/beta.hpp @@ -1674,7 +1674,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b } else { - return invert ? (normalised ? std::log1p(-fract) : log(boost::math::beta(a, b, pol) - fract)) : log(fract); + return invert ? (normalised ? boost::math::log1p(-fract) : log(boost::math::beta(a, b, pol) - fract)) : log(fract); } } // template T ibeta_imp(T a, T b, T x, const Lanczos& l, bool inv, bool normalised) From 9b295522f5b7b3d28f4d6cc5488a836ef2335d01 Mon Sep 17 00:00:00 2001 From: Jacob Hass Date: Tue, 10 Feb 2026 20:33:57 -0800 Subject: [PATCH 3/5] Added lightweight test_ibeta to Jamfile.v2 --- test/Jamfile.v2 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c5bde01635..355a1bb088 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -263,6 +263,7 @@ test-suite special_fun : [ run test_gamma_mp.cpp /boost/test//boost_unit_test_framework : : : release TEST=3 [ check-target-builds ../config//is_ci_sanitizer_run "Sanitizer CI run" : no ] : test_gamma_mp_3 ] [ run test_hankel.cpp /boost/test//boost_unit_test_framework ] [ run test_hermite.cpp test_instances//test_instances pch_light /boost/test//boost_unit_test_framework ] + [ run test_ibeta.cpp test_instances//test_instances pch_light /boost/test//boost_unit_test_framework ] [ run test_ibeta.cpp test_instances//test_instances pch_light /boost/test//boost_unit_test_framework : # command line : # input files From 4b33948fc74546ca67af011e2d4130884f2b94f6 Mon Sep 17 00:00:00 2001 From: Jacob Hass Date: Tue, 10 Feb 2026 20:34:19 -0800 Subject: [PATCH 4/5] Added grid testing for log_ibeta --- test/test_ibeta.hpp | 80 +++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/test/test_ibeta.hpp b/test/test_ibeta.hpp index 2f0ea47107..d5f8c0d2c8 100644 --- a/test/test_ibeta.hpp +++ b/test/test_ibeta.hpp @@ -453,39 +453,55 @@ void test_spots(T) static_cast(4), ldexp(static_cast(1 + static_cast(1.0) / 1024), -351)), static_cast(2.381008060978474962211278613067275529112106932635520021e-212L), tolerance); - BOOST_CHECK_CLOSE( - ::boost::math::beta( - static_cast(2), - static_cast(4), - ldexp(static_cast(1 + static_cast(1.0) / 2048), -351)), - static_cast(2.378685692854274898232669682422430136513931911501225435e-212L), tolerance); - BOOST_CHECK_CLOSE( - ::boost::math::ibeta( - static_cast(3), - static_cast(5), - ldexp(static_cast(1 + static_cast(15) / 16), -268)), - static_cast(2.386034198603463687323052353589201848077110231388968865e-240L), tolerance); - BOOST_CHECK_CLOSE( - ::boost::math::ibeta_derivative( - static_cast(2), - static_cast(4), - ldexp(static_cast(1), -557)), - static_cast(4.23957586190238472641508753637420672781472122471791800210e-167L), tolerance * 4); - BOOST_CHECK_CLOSE( - ::boost::math::ibeta_derivative( - static_cast(2), - static_cast(4.5), - ldexp(static_cast(1), -557)), - static_cast(5.24647512910420109893867082626308082567071751558842352760e-167L), tolerance * 20); - - - T tiny = boost::math::tools::min_value() / 2; - T small = boost::math::tools::epsilon(); - if (tiny != 0) + BOOST_CHECK_CLOSE( + ::boost::math::beta( + static_cast(2), + static_cast(4), + ldexp(static_cast(1 + static_cast(1.0) / 2048), -351)), + static_cast(2.378685692854274898232669682422430136513931911501225435e-212L), tolerance); + BOOST_CHECK_CLOSE( + ::boost::math::ibeta( + static_cast(3), + static_cast(5), + ldexp(static_cast(1 + static_cast(15) / 16), -268)), + static_cast(2.386034198603463687323052353589201848077110231388968865e-240L), tolerance); + BOOST_CHECK_CLOSE( + ::boost::math::ibeta_derivative( + static_cast(2), + static_cast(4), + ldexp(static_cast(1), -557)), + static_cast(4.23957586190238472641508753637420672781472122471791800210e-167L), tolerance * 4); + BOOST_CHECK_CLOSE( + ::boost::math::ibeta_derivative( + static_cast(2), + static_cast(4.5), + ldexp(static_cast(1), -557)), + static_cast(5.24647512910420109893867082626308082567071751558842352760e-167L), tolerance * 20); + + // Test log_ibeta special function + // First check log_ibeta matches ibeta on a grid + + T a_vals[] = { 0.25, 1., 5., 10., 50.}; + T x_vals[] = { 0.0078125, 0.0625, 0.25, 0.75}; + + for (T a : a_vals) + { + for (T b : a_vals) { - BOOST_CHECK_EQUAL(boost::math::ibeta(tiny, small, small), 1); + for (T x : x_vals) + { + BOOST_CHECK_CLOSE(exp(::boost::math::log_ibeta(a, b, x)), ::boost::math::ibeta(a, b, x), tolerance); + } } - BOOST_CHECK_EQUAL(boost::math::ibeta(static_cast(2), static_cast(1), static_cast(0)), 0); - BOOST_CHECK_EQUAL(boost::math::ibeta(static_cast(1), static_cast(2), static_cast(0)), 0); + } + + T tiny = boost::math::tools::min_value() / 2; + T small = boost::math::tools::epsilon(); + if (tiny != 0) + { + BOOST_CHECK_EQUAL(boost::math::ibeta(tiny, small, small), 1); + } + BOOST_CHECK_EQUAL(boost::math::ibeta(static_cast(2), static_cast(1), static_cast(0)), 0); + BOOST_CHECK_EQUAL(boost::math::ibeta(static_cast(1), static_cast(2), static_cast(0)), 0); } From f1c2e6fbb991e324307fe4b885f880d288471615 Mon Sep 17 00:00:00 2001 From: Jacob Hass Date: Wed, 11 Feb 2026 08:54:34 -0800 Subject: [PATCH 5/5] Changed return infinity to throwing overflow error --- include/boost/math/special_functions/beta.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/math/special_functions/beta.hpp b/include/boost/math/special_functions/beta.hpp index 9e1e82e9a5..88ab8462f9 100644 --- a/include/boost/math/special_functions/beta.hpp +++ b/include/boost/math/special_functions/beta.hpp @@ -1242,7 +1242,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b } else { - return inv ? -std::numeric_limits::infinity() : T(0); + return inv ? -policies::raise_overflow_error(function, nullptr, pol) : T(0); } } @@ -1258,7 +1258,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b } else { - return inv ? T(0) : -std::numeric_limits::infinity(); + return inv ? T(0) : -policies::raise_overflow_error(function, nullptr, pol); } } } @@ -1283,7 +1283,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b } else { - return (invert ? (normalised ? 0 : log(boost::math::beta(a, b, pol))) : -std::numeric_limits::infinity()); + return (invert ? (normalised ? 0 : log(boost::math::beta(a, b, pol))) : -policies::raise_overflow_error(function, nullptr, pol)); } } if(x == 1) @@ -1298,7 +1298,7 @@ BOOST_MATH_GPU_ENABLED T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, b } else { - return (invert == 0 ? (normalised ? 0 : log(boost::math::beta(a, b, pol))) : -std::numeric_limits::infinity()); + return (invert == 0 ? (normalised ? 0 : log(boost::math::beta(a, b, pol))) : -policies::raise_overflow_error(function, nullptr, pol)); } }