From aa445f6bf3654810d84c6770ead82c2f9e4bb6fe Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 10:54:46 +0200 Subject: [PATCH 01/23] Implement logistic function --- .../boost/math/special_functions/logistic.hpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 include/boost/math/special_functions/logistic.hpp diff --git a/include/boost/math/special_functions/logistic.hpp b/include/boost/math/special_functions/logistic.hpp new file mode 100644 index 0000000000..81c6a7c04e --- /dev/null +++ b/include/boost/math/special_functions/logistic.hpp @@ -0,0 +1,41 @@ +// Copyright Matt Borland 2025. +// 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_SF_EXPIT_HPP +#define BOOST_MATH_SF_EXPIT_HPP + +#include +#include +#include + +namespace boost { +namespace math { + +template +RealType logistic(RealType x, const Policy&) +{ + BOOST_MATH_STD_USING + + using promoted_real_type = typename policies::evaluation::type; + + std::fexcept_t flags; + std::fegetexceptflag(&flags, FE_ALL_EXCEPT); + const auto res {static_cast(1 / (1 + exp(static_cast(-x))))}; + std::fesetexceptflag(&flags, FE_ALL_EXCEPT); + + return res; +} + +template +RealType logistic(RealType x) +{ + return logistic(x, policies::policy<>()); +} + +} // namespace math +} // namespace boost + +#endif // BOOST_MATH_SF_EXPIT_HPP From 3ffbcfea310f07c961d82f7c02e194adac616beb Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 10:55:01 +0200 Subject: [PATCH 02/23] Add logistic function testing --- test/Jamfile.v2 | 1 + test/test_logistic.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 test/test_logistic.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 7a7be3f5d9..dd8cdec330 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -590,6 +590,7 @@ test-suite special_fun : [ run test_sinc.cpp /boost/test//boost_unit_test_framework pch_light ] [ run test_fibonacci.cpp /boost/test//boost_unit_test_framework ] [ run test_prime.cpp /boost/test//boost_unit_test_framework ] + [ run test_logistic.cpp ] ; test-suite distribution_tests : diff --git a/test/test_logistic.cpp b/test/test_logistic.cpp new file mode 100644 index 0000000000..a47df70fcc --- /dev/null +++ b/test/test_logistic.cpp @@ -0,0 +1,84 @@ +// Copyright Matt Borland 2025. +// 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 "math_unit_test.hpp" +#include +#include + +template +void test() +{ + const std::array x_values = { + 0, + 1, + 1000, + 0.5, + 0.75 + }; + const std::array y_values = { + RealType{1} / 2, + RealType{0.73105857863000487925115924182183627436514464016505651927636590791904045307}, + RealType{1}, + RealType{0.62245933120185456463890056574550847875327936530891016305943716265854500}, + RealType{0.6791786991753929731596801157765790212342212482195760219829517436805} + }; + + for (std::size_t i = 0; i < x_values.size(); ++i) + { + const RealType test_value {boost::math::logistic(x_values[i])}; + BOOST_MATH_IF_CONSTEXPR (std::is_arithmetic::value) + { + CHECK_ULP_CLOSE(test_value, y_values[i], 1); + } + else + { + CHECK_MOLLIFIED_CLOSE(test_value, y_values[i], 1e-15); + } + + bool fe {false}; + if (std::fetestexcept(FE_OVERFLOW)) + { + fe = true; + std::cerr << "FE_OVERFLOW" << std::endl; + } + if (std::fetestexcept(FE_UNDERFLOW)) + { + fe = true; + std::cerr << "FE_UNDERFLOW" << std::endl; + } + if (std::fetestexcept(FE_DIVBYZERO)) + { + fe = true; + std::cerr << "FE_DIVBYZERO" << std::endl; + } + if (std::fetestexcept(FE_INVALID)) + { + fe = true; + std::cerr << "FE_INVALID" << std::endl; + } + + CHECK_EQUAL(fe, false); + } +} + +int main() +{ + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + return boost::math::test::report_errors(); +} From bb699fa5ca87f48150b99a1f68acedc879060aad Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 11:21:51 +0200 Subject: [PATCH 03/23] Implement logit function --- .../boost/math/special_functions/logit.hpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 include/boost/math/special_functions/logit.hpp diff --git a/include/boost/math/special_functions/logit.hpp b/include/boost/math/special_functions/logit.hpp new file mode 100644 index 0000000000..ed07986960 --- /dev/null +++ b/include/boost/math/special_functions/logit.hpp @@ -0,0 +1,54 @@ +// Copyright Matt Borland 2025. +// 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_SF_LOGIT_HPP +#define BOOST_MATH_SF_LOGIT_HPP + +#include +#include +#include +#include + +namespace boost { +namespace math { + +template +RealType logit(RealType p, const Policy&) +{ + BOOST_MATH_STD_USING + + using promoted_real_type = typename policies::evaluation::type; + + std::fexcept_t flags; + std::fegetexceptflag(&flags, FE_ALL_EXCEPT); + + static const RealType crossover {RealType{1}/4}; + const auto promoted_p {static_cast(p)}; + RealType result {}; + if (p > crossover) + { + result = 2 * atanh(2 * promoted_p - 1); + } + else + { + result = log((1 - promoted_p) / promoted_p); + } + + std::fesetexceptflag(&flags, FE_ALL_EXCEPT); + + return result; +} + +template +RealType logit(RealType p) +{ + return logit(p, policies::policy<>()); +} + +} // namespace math +} // namespace boost + +#endif // BOOST_MATH_SF_LOGIT_HPP From 2e6684970311da04c77c674c04c3768813c31b68 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 11:32:22 +0200 Subject: [PATCH 04/23] Rename --- .../{logistic.hpp => logistic_sigmoid.hpp} | 6 +++--- test/Jamfile.v2 | 2 +- test/{test_logistic.cpp => test_logistic_sigmoid.cpp} | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename include/boost/math/special_functions/{logistic.hpp => logistic_sigmoid.hpp} (86%) rename test/{test_logistic.cpp => test_logistic_sigmoid.cpp} (93%) diff --git a/include/boost/math/special_functions/logistic.hpp b/include/boost/math/special_functions/logistic_sigmoid.hpp similarity index 86% rename from include/boost/math/special_functions/logistic.hpp rename to include/boost/math/special_functions/logistic_sigmoid.hpp index 81c6a7c04e..5b73eef2c3 100644 --- a/include/boost/math/special_functions/logistic.hpp +++ b/include/boost/math/special_functions/logistic_sigmoid.hpp @@ -15,7 +15,7 @@ namespace boost { namespace math { template -RealType logistic(RealType x, const Policy&) +RealType logistic_sigmoid(RealType x, const Policy&) { BOOST_MATH_STD_USING @@ -30,9 +30,9 @@ RealType logistic(RealType x, const Policy&) } template -RealType logistic(RealType x) +RealType logistic_sigmoid(RealType x) { - return logistic(x, policies::policy<>()); + return logistic_sigmoid(x, policies::policy<>()); } } // namespace math diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index dd8cdec330..41be0f8bbf 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -590,7 +590,7 @@ test-suite special_fun : [ run test_sinc.cpp /boost/test//boost_unit_test_framework pch_light ] [ run test_fibonacci.cpp /boost/test//boost_unit_test_framework ] [ run test_prime.cpp /boost/test//boost_unit_test_framework ] - [ run test_logistic.cpp ] + [ run test_logistic_sigmoid.cpp ] ; test-suite distribution_tests : diff --git a/test/test_logistic.cpp b/test/test_logistic_sigmoid.cpp similarity index 93% rename from test/test_logistic.cpp rename to test/test_logistic_sigmoid.cpp index a47df70fcc..fd9b7b366b 100644 --- a/test/test_logistic.cpp +++ b/test/test_logistic_sigmoid.cpp @@ -4,7 +4,7 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#include +#include #include #include "math_unit_test.hpp" #include @@ -30,7 +30,7 @@ void test() for (std::size_t i = 0; i < x_values.size(); ++i) { - const RealType test_value {boost::math::logistic(x_values[i])}; + const RealType test_value {boost::math::logistic_sigmoid(x_values[i])}; BOOST_MATH_IF_CONSTEXPR (std::is_arithmetic::value) { CHECK_ULP_CLOSE(test_value, y_values[i], 1); From c26f50fa0d9e50a0a3f5193f673228a8aeb59eb7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 11:35:46 +0200 Subject: [PATCH 05/23] Add missing pragma --- test/test_logistic_sigmoid.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_logistic_sigmoid.cpp b/test/test_logistic_sigmoid.cpp index fd9b7b366b..857006e4a9 100644 --- a/test/test_logistic_sigmoid.cpp +++ b/test/test_logistic_sigmoid.cpp @@ -10,6 +10,8 @@ #include #include +#pragma STDC FENV_ACCESS ON + template void test() { From ce144e22fdd6722ec53ed714f2b86846f2eb78a1 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 12:49:22 +0200 Subject: [PATCH 06/23] Add logit test set --- test/Jamfile.v2 | 1 + test/test_logit.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 test/test_logit.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 41be0f8bbf..3843264429 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -591,6 +591,7 @@ test-suite special_fun : [ run test_fibonacci.cpp /boost/test//boost_unit_test_framework ] [ run test_prime.cpp /boost/test//boost_unit_test_framework ] [ run test_logistic_sigmoid.cpp ] + [ run test_logit.cpp ] ; test-suite distribution_tests : diff --git a/test/test_logit.cpp b/test/test_logit.cpp new file mode 100644 index 0000000000..fb2c67c5d0 --- /dev/null +++ b/test/test_logit.cpp @@ -0,0 +1,87 @@ +// Copyright Matt Borland 2025. +// 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 "math_unit_test.hpp" +#include +#include + +#pragma STDC FENV_ACCESS ON + +template +void test() +{ + const std::array x_values = { + 0.01, + 0.24, + 0.5, + 0.75, + 0.995, + }; + const std::array y_values = { + RealType{-4.595119850134589926852434051810180709116687969582916078687956376405}, + RealType{-1.15267950993838545919655007350715126451438856911612411268258589327840479}, + RealType{0}, + RealType{1.09861228866810969139524523692252570464749055782274945173469433363749429}, + RealType{5.2933048247244923954101212918685372018911052805694724989064609879440992} + }; + + for (std::size_t i = 0; i < x_values.size(); ++i) + { + const RealType test_value {boost::math::logit(x_values[i])}; + + BOOST_MATH_IF_CONSTEXPR (std::is_arithmetic::value) + { + CHECK_ULP_CLOSE(test_value, y_values[i], 5); + } + else + { + CHECK_MOLLIFIED_CLOSE(test_value, y_values[i], 1e-15); + } + + bool fe {false}; + if (std::fetestexcept(FE_OVERFLOW)) + { + fe = true; + std::cerr << "FE_OVERFLOW" << std::endl; + } + if (std::fetestexcept(FE_UNDERFLOW)) + { + fe = true; + std::cerr << "FE_UNDERFLOW" << std::endl; + } + if (std::fetestexcept(FE_DIVBYZERO)) + { + fe = true; + std::cerr << "FE_DIVBYZERO" << std::endl; + } + if (std::fetestexcept(FE_INVALID)) + { + fe = true; + std::cerr << "FE_INVALID" << std::endl; + } + + CHECK_EQUAL(fe, false); + } +} + +int main() +{ + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + std::feclearexcept(FE_ALL_EXCEPT); + test(); + + return boost::math::test::report_errors(); +} From 064ca93d06b27887652aac7bc32fb45c55f1c657 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 12:49:36 +0200 Subject: [PATCH 07/23] Explicitly cast to result --- include/boost/math/special_functions/logit.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/math/special_functions/logit.hpp b/include/boost/math/special_functions/logit.hpp index ed07986960..159042f46d 100644 --- a/include/boost/math/special_functions/logit.hpp +++ b/include/boost/math/special_functions/logit.hpp @@ -30,11 +30,11 @@ RealType logit(RealType p, const Policy&) RealType result {}; if (p > crossover) { - result = 2 * atanh(2 * promoted_p - 1); + result = static_cast(2 * atanh(2 * promoted_p - 1)); } else { - result = log((1 - promoted_p) / promoted_p); + result = static_cast(log(promoted_p / (1 - promoted_p))); } std::fesetexceptflag(&flags, FE_ALL_EXCEPT); From b33d31ae9d243f7d8d8ede0920f6b2298898c4e3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 12:55:18 +0200 Subject: [PATCH 08/23] Use new functions with logisitic distribution --- include/boost/math/distributions/logistic.hpp | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/include/boost/math/distributions/logistic.hpp b/include/boost/math/distributions/logistic.hpp index 69ec818abe..1b7245f63b 100644 --- a/include/boost/math/distributions/logistic.hpp +++ b/include/boost/math/distributions/logistic.hpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include namespace boost { namespace math { @@ -150,7 +152,7 @@ namespace boost { namespace math { return 0; if(power < -tools::log_max_value()) return 1; - return 1 / (1 + exp(power)); + return logistic_sigmoid(-power, Policy()); } template @@ -221,15 +223,8 @@ namespace boost { namespace math { { return policies::raise_overflow_error(function,"probability argument is 1, must be >0 and <1",Policy()); } - //Expressions to try - //return location+scale*log(p/(1-p)); - //return location+scale*log1p((2*p-1)/(1-p)); - //return location - scale*log( (1-p)/p); - //return location - scale*log1p((1-2*p)/p); - - //return -scale*log(1/p-1) + location; - return location - scale * log((1 - p) / p); + return location - scale * -logit(p, Policy()); } // RealType quantile(const logistic_distribution& dist, const RealType& p) template @@ -264,7 +259,7 @@ namespace boost { namespace math { return 0; if(power < -tools::log_max_value()) return 1; - return 1 / (1 + exp(power)); + return logistic_sigmoid(-power, Policy()); } template @@ -328,15 +323,8 @@ namespace boost { namespace math { { return policies::raise_overflow_error(function,"probability argument is 0, but must be >0 and <1",Policy()); } - //Expressions to try - //return location+scale*log((1-q)/q); - return location + scale * log((1 - q) / q); - - //return location-scale*log(q/(1-q)); - //return location-scale*log1p((2*q-1)/(1-q)); - //return location+scale*log(1/q-1); - //return location+scale*log1p(1/q-2); + return location + scale * -logit(q, Policy()); } template From f3a823d544999f028f0259f5c368b7686c5bf0f2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 13:35:01 +0200 Subject: [PATCH 09/23] Add reproducer for github issue 1294 --- .../boost/math/special_functions/logit.hpp | 1 + test/git_issue_1294.cpp | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/git_issue_1294.cpp diff --git a/include/boost/math/special_functions/logit.hpp b/include/boost/math/special_functions/logit.hpp index 159042f46d..3ffa303963 100644 --- a/include/boost/math/special_functions/logit.hpp +++ b/include/boost/math/special_functions/logit.hpp @@ -19,6 +19,7 @@ template RealType logit(RealType p, const Policy&) { BOOST_MATH_STD_USING + using std::atanh; using promoted_real_type = typename policies::evaluation::type; diff --git a/test/git_issue_1294.cpp b/test/git_issue_1294.cpp new file mode 100644 index 0000000000..047a8ea7b3 --- /dev/null +++ b/test/git_issue_1294.cpp @@ -0,0 +1,33 @@ +// Copyright 2025 Matt Borland +// 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) +// +// See: https://github.com/boostorg/math/issues/1294 + +#include +#include "math_unit_test.hpp" + +int main() +{ + using namespace boost::math::policies; + using boost::math::logistic_distribution; + + typedef policy< + promote_float, + promote_double + > with_promotion; + + constexpr double p = 2049.0/4096; + constexpr double ref = 9.76562577610225755e-04; + + logistic_distribution dist_promote; + const double x = quantile(dist_promote, p); + + // Previously we had: 9.76562577610170027e-04 + // Which is an ULP distance of 256 + CHECK_ULP_CLOSE(x, ref, 1); + + return boost::math::test::report_errors(); +} From a3d22ea8e2e50465a5302405faa23220330df531 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 14:08:53 +0200 Subject: [PATCH 10/23] Adjust tolerances --- test/test_logistic_sigmoid.cpp | 2 +- test/test_logit.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_logistic_sigmoid.cpp b/test/test_logistic_sigmoid.cpp index 857006e4a9..01498fffb2 100644 --- a/test/test_logistic_sigmoid.cpp +++ b/test/test_logistic_sigmoid.cpp @@ -33,7 +33,7 @@ void test() for (std::size_t i = 0; i < x_values.size(); ++i) { const RealType test_value {boost::math::logistic_sigmoid(x_values[i])}; - BOOST_MATH_IF_CONSTEXPR (std::is_arithmetic::value) + BOOST_MATH_IF_CONSTEXPR (std::is_same::value || std::is_same::value) { CHECK_ULP_CLOSE(test_value, y_values[i], 1); } diff --git a/test/test_logit.cpp b/test/test_logit.cpp index fb2c67c5d0..a89bcae56e 100644 --- a/test/test_logit.cpp +++ b/test/test_logit.cpp @@ -34,7 +34,7 @@ void test() { const RealType test_value {boost::math::logit(x_values[i])}; - BOOST_MATH_IF_CONSTEXPR (std::is_arithmetic::value) + BOOST_MATH_IF_CONSTEXPR (std::is_same::value || std::is_same::value) { CHECK_ULP_CLOSE(test_value, y_values[i], 5); } From 6068d7a95a8a8399af18d52e9e4be91c123cb38b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 14:10:32 +0200 Subject: [PATCH 11/23] Only adjust the FPU flags when we have access --- include/boost/math/special_functions/logistic_sigmoid.hpp | 6 ++++++ include/boost/math/special_functions/logit.hpp | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/include/boost/math/special_functions/logistic_sigmoid.hpp b/include/boost/math/special_functions/logistic_sigmoid.hpp index 5b73eef2c3..5bb79c101c 100644 --- a/include/boost/math/special_functions/logistic_sigmoid.hpp +++ b/include/boost/math/special_functions/logistic_sigmoid.hpp @@ -21,10 +21,16 @@ RealType logistic_sigmoid(RealType x, const Policy&) using promoted_real_type = typename policies::evaluation::type; + #ifndef BOOST_MATH_HAS_GPU_SUPPORT std::fexcept_t flags; std::fegetexceptflag(&flags, FE_ALL_EXCEPT); + #endif + const auto res {static_cast(1 / (1 + exp(static_cast(-x))))}; + + #ifndef BOOST_MATH_HAS_GPU_SUPPORT std::fesetexceptflag(&flags, FE_ALL_EXCEPT); + #endif return res; } diff --git a/include/boost/math/special_functions/logit.hpp b/include/boost/math/special_functions/logit.hpp index 3ffa303963..80f51d7b08 100644 --- a/include/boost/math/special_functions/logit.hpp +++ b/include/boost/math/special_functions/logit.hpp @@ -23,8 +23,10 @@ RealType logit(RealType p, const Policy&) using promoted_real_type = typename policies::evaluation::type; + #ifndef BOOST_MATH_HAS_GPU_SUPPORT std::fexcept_t flags; std::fegetexceptflag(&flags, FE_ALL_EXCEPT); + #endif static const RealType crossover {RealType{1}/4}; const auto promoted_p {static_cast(p)}; @@ -38,7 +40,9 @@ RealType logit(RealType p, const Policy&) result = static_cast(log(promoted_p / (1 - promoted_p))); } + #ifndef BOOST_MATH_HAS_GPU_SUPPORT std::fesetexceptflag(&flags, FE_ALL_EXCEPT); + #endif return result; } From 488cd2fd11467175ee2772f57a8cc27faa1ec714 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 15:03:43 +0200 Subject: [PATCH 12/23] Action review comments replacing fenv manipulation --- include/boost/math/distributions/logistic.hpp | 8 ------- .../special_functions/logistic_sigmoid.hpp | 19 ++++++++-------- .../boost/math/special_functions/logit.hpp | 13 +++++------ test/test_logistic_sigmoid.cpp | 1 + test/test_logit.cpp | 22 ++++++++++++++++++- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/include/boost/math/distributions/logistic.hpp b/include/boost/math/distributions/logistic.hpp index 1b7245f63b..1390b74e56 100644 --- a/include/boost/math/distributions/logistic.hpp +++ b/include/boost/math/distributions/logistic.hpp @@ -148,10 +148,6 @@ namespace boost { namespace math { } BOOST_MATH_STD_USING RealType power = (location - x) / scale; - if(power > tools::log_max_value()) - return 0; - if(power < -tools::log_max_value()) - return 1; return logistic_sigmoid(-power, Policy()); } @@ -255,10 +251,6 @@ namespace boost { namespace math { return result; } RealType power = (x - location) / scale; - if(power > tools::log_max_value()) - return 0; - if(power < -tools::log_max_value()) - return 1; return logistic_sigmoid(-power, Policy()); } diff --git a/include/boost/math/special_functions/logistic_sigmoid.hpp b/include/boost/math/special_functions/logistic_sigmoid.hpp index 5bb79c101c..9bcd998f95 100644 --- a/include/boost/math/special_functions/logistic_sigmoid.hpp +++ b/include/boost/math/special_functions/logistic_sigmoid.hpp @@ -8,8 +8,8 @@ #define BOOST_MATH_SF_EXPIT_HPP #include +#include #include -#include namespace boost { namespace math { @@ -21,17 +21,16 @@ RealType logistic_sigmoid(RealType x, const Policy&) using promoted_real_type = typename policies::evaluation::type; - #ifndef BOOST_MATH_HAS_GPU_SUPPORT - std::fexcept_t flags; - std::fegetexceptflag(&flags, FE_ALL_EXCEPT); - #endif + if(-x >= tools::log_max_value()) + { + return 0; + } + if(-x <= -tools::log_max_value()) + { + return 1; + } const auto res {static_cast(1 / (1 + exp(static_cast(-x))))}; - - #ifndef BOOST_MATH_HAS_GPU_SUPPORT - std::fesetexceptflag(&flags, FE_ALL_EXCEPT); - #endif - return res; } diff --git a/include/boost/math/special_functions/logit.hpp b/include/boost/math/special_functions/logit.hpp index 80f51d7b08..8a2ddf68dd 100644 --- a/include/boost/math/special_functions/logit.hpp +++ b/include/boost/math/special_functions/logit.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -23,10 +24,10 @@ RealType logit(RealType p, const Policy&) using promoted_real_type = typename policies::evaluation::type; - #ifndef BOOST_MATH_HAS_GPU_SUPPORT - std::fexcept_t flags; - std::fegetexceptflag(&flags, FE_ALL_EXCEPT); - #endif + if (p < tools::min_value()) + { + return -policies::raise_overflow_error("logit", "sub-normals will overflow ln(x/(1-x))", Policy()); + } static const RealType crossover {RealType{1}/4}; const auto promoted_p {static_cast(p)}; @@ -40,10 +41,6 @@ RealType logit(RealType p, const Policy&) result = static_cast(log(promoted_p / (1 - promoted_p))); } - #ifndef BOOST_MATH_HAS_GPU_SUPPORT - std::fesetexceptflag(&flags, FE_ALL_EXCEPT); - #endif - return result; } diff --git a/test/test_logistic_sigmoid.cpp b/test/test_logistic_sigmoid.cpp index 01498fffb2..6a8814be59 100644 --- a/test/test_logistic_sigmoid.cpp +++ b/test/test_logistic_sigmoid.cpp @@ -9,6 +9,7 @@ #include "math_unit_test.hpp" #include #include +#include #pragma STDC FENV_ACCESS ON diff --git a/test/test_logit.cpp b/test/test_logit.cpp index a89bcae56e..1910b2aa17 100644 --- a/test/test_logit.cpp +++ b/test/test_logit.cpp @@ -8,7 +8,8 @@ #include #include "math_unit_test.hpp" #include -#include +#include +#include #pragma STDC FENV_ACCESS ON @@ -67,6 +68,25 @@ void test() CHECK_EQUAL(fe, false); } + + #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) + + BOOST_MATH_IF_CONSTEXPR (std::is_arithmetic::value) + { + bool thrown {false}; + try + { + boost::math::logit(std::numeric_limits::denorm_min()); + } + catch (...) + { + thrown = true; + } + + CHECK_EQUAL(thrown, true); + } + + #endif // Exceptional environments } int main() From a453cb36c3541347e14810970e2c058eb87058e7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 16:41:13 +0200 Subject: [PATCH 13/23] Ignore lines that should not be hit --- test/test_logistic_sigmoid.cpp | 16 ++++++++-------- test/test_logit.cpp | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/test_logistic_sigmoid.cpp b/test/test_logistic_sigmoid.cpp index 6a8814be59..e3b0560701 100644 --- a/test/test_logistic_sigmoid.cpp +++ b/test/test_logistic_sigmoid.cpp @@ -46,23 +46,23 @@ void test() bool fe {false}; if (std::fetestexcept(FE_OVERFLOW)) { - fe = true; - std::cerr << "FE_OVERFLOW" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_OVERFLOW" << std::endl; // LCOV_EXCL_LINE } if (std::fetestexcept(FE_UNDERFLOW)) { - fe = true; - std::cerr << "FE_UNDERFLOW" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_UNDERFLOW" << std::endl; // LCOV_EXCL_LINE } if (std::fetestexcept(FE_DIVBYZERO)) { - fe = true; - std::cerr << "FE_DIVBYZERO" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_DIVBYZERO" << std::endl; // LCOV_EXCL_LINE } if (std::fetestexcept(FE_INVALID)) { - fe = true; - std::cerr << "FE_INVALID" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_INVALID" << std::endl; // LCOV_EXCL_LINE } CHECK_EQUAL(fe, false); diff --git a/test/test_logit.cpp b/test/test_logit.cpp index 1910b2aa17..bef357e87a 100644 --- a/test/test_logit.cpp +++ b/test/test_logit.cpp @@ -47,23 +47,23 @@ void test() bool fe {false}; if (std::fetestexcept(FE_OVERFLOW)) { - fe = true; - std::cerr << "FE_OVERFLOW" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_OVERFLOW" << std::endl; // LCOV_EXCL_LINE } if (std::fetestexcept(FE_UNDERFLOW)) { - fe = true; - std::cerr << "FE_UNDERFLOW" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_UNDERFLOW" << std::endl; // LCOV_EXCL_LINE } if (std::fetestexcept(FE_DIVBYZERO)) { - fe = true; - std::cerr << "FE_DIVBYZERO" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_DIVBYZERO" << std::endl; // LCOV_EXCL_LINE } if (std::fetestexcept(FE_INVALID)) { - fe = true; - std::cerr << "FE_INVALID" << std::endl; + fe = true; // LCOV_EXCL_LINE + std::cerr << "FE_INVALID" << std::endl; // LCOV_EXCL_LINE } CHECK_EQUAL(fe, false); From 4c086e6cbad1d920ac389d7cef80b221148c9f8f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 16:56:43 +0200 Subject: [PATCH 14/23] Fix return types for concept tests --- include/boost/math/special_functions/logistic_sigmoid.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/math/special_functions/logistic_sigmoid.hpp b/include/boost/math/special_functions/logistic_sigmoid.hpp index 9bcd998f95..8404beb78b 100644 --- a/include/boost/math/special_functions/logistic_sigmoid.hpp +++ b/include/boost/math/special_functions/logistic_sigmoid.hpp @@ -23,11 +23,11 @@ RealType logistic_sigmoid(RealType x, const Policy&) if(-x >= tools::log_max_value()) { - return 0; + return RealType{0}; } if(-x <= -tools::log_max_value()) { - return 1; + return RealType{1}; } const auto res {static_cast(1 / (1 + exp(static_cast(-x))))}; From 6893b7427285c75552053474efb40ac8d33a924b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 17:05:38 +0200 Subject: [PATCH 15/23] Add and fix additional test case --- .../math/special_functions/logistic_sigmoid.hpp | 4 ++-- test/math_unit_test.hpp | 2 +- test/test_logistic_sigmoid.cpp | 16 ++++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/boost/math/special_functions/logistic_sigmoid.hpp b/include/boost/math/special_functions/logistic_sigmoid.hpp index 8404beb78b..925b576ca3 100644 --- a/include/boost/math/special_functions/logistic_sigmoid.hpp +++ b/include/boost/math/special_functions/logistic_sigmoid.hpp @@ -23,11 +23,11 @@ RealType logistic_sigmoid(RealType x, const Policy&) if(-x >= tools::log_max_value()) { - return RealType{0}; + return static_cast(0); } if(-x <= -tools::log_max_value()) { - return RealType{1}; + return static_cast(1); } const auto res {static_cast(1 / (1 + exp(static_cast(-x))))}; diff --git a/test/math_unit_test.hpp b/test/math_unit_test.hpp index 648f7de9c5..bd85caa86b 100644 --- a/test/math_unit_test.hpp +++ b/test/math_unit_test.hpp @@ -58,7 +58,7 @@ bool check_mollified_close(Real expected, Real computed, Real tol, std::string c } using std::max; using std::abs; - Real denom = (max)(abs(expected), Real(1)); + Real denom = (max)(Real(abs(expected)), Real(1)); Real mollified_relative_error = abs(expected - computed)/denom; if (mollified_relative_error > tol) { diff --git a/test/test_logistic_sigmoid.cpp b/test/test_logistic_sigmoid.cpp index e3b0560701..c0c8a04920 100644 --- a/test/test_logistic_sigmoid.cpp +++ b/test/test_logistic_sigmoid.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "math_unit_test.hpp" #include #include @@ -24,11 +25,11 @@ void test() 0.75 }; const std::array y_values = { - RealType{1} / 2, - RealType{0.73105857863000487925115924182183627436514464016505651927636590791904045307}, - RealType{1}, - RealType{0.62245933120185456463890056574550847875327936530891016305943716265854500}, - RealType{0.6791786991753929731596801157765790212342212482195760219829517436805} + static_cast(1) / 2, + static_cast(0.73105857863000487925115924182183627436514464016505651927636590791904045307), + static_cast(1), + static_cast(0.62245933120185456463890056574550847875327936530891016305943716265854500), + static_cast(0.6791786991753929731596801157765790212342212482195760219829517436805) }; for (std::size_t i = 0; i < x_values.size(); ++i) @@ -40,7 +41,8 @@ void test() } else { - CHECK_MOLLIFIED_CLOSE(test_value, y_values[i], 1e-15); + RealType comparison_value = y_values[i]; + CHECK_MOLLIFIED_CLOSE(test_value, comparison_value, static_cast(1e-15)); } bool fe {false}; @@ -83,5 +85,7 @@ int main() std::feclearexcept(FE_ALL_EXCEPT); test(); + test(); + return boost::math::test::report_errors(); } From 79ab23ffa4b330fd65f3262c426015ee6d4dd1ea Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 12 Aug 2025 17:49:48 +0200 Subject: [PATCH 16/23] Move sign position --- include/boost/math/distributions/logistic.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/math/distributions/logistic.hpp b/include/boost/math/distributions/logistic.hpp index 1390b74e56..30cffe8ae4 100644 --- a/include/boost/math/distributions/logistic.hpp +++ b/include/boost/math/distributions/logistic.hpp @@ -147,8 +147,8 @@ namespace boost { namespace math { return result; } BOOST_MATH_STD_USING - RealType power = (location - x) / scale; - return logistic_sigmoid(-power, Policy()); + RealType power = -(location - x) / scale; + return logistic_sigmoid(power, Policy()); } template @@ -250,8 +250,8 @@ namespace boost { namespace math { { return result; } - RealType power = (x - location) / scale; - return logistic_sigmoid(-power, Policy()); + RealType power = -(x - location) / scale; + return logistic_sigmoid(power, Policy()); } template From 24e59fdabb459671f9df8c99a7cc6decabfb3741 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 13 Aug 2025 10:47:49 +0200 Subject: [PATCH 17/23] Remove double negatives and now excess using statements --- include/boost/math/distributions/logistic.hpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/include/boost/math/distributions/logistic.hpp b/include/boost/math/distributions/logistic.hpp index 30cffe8ae4..1f8cb46fc3 100644 --- a/include/boost/math/distributions/logistic.hpp +++ b/include/boost/math/distributions/logistic.hpp @@ -146,8 +146,8 @@ namespace boost { namespace math { { return result; } - BOOST_MATH_STD_USING - RealType power = -(location - x) / scale; + + RealType power = (x - location) / scale; return logistic_sigmoid(power, Policy()); } @@ -197,7 +197,6 @@ namespace boost { namespace math { template BOOST_MATH_GPU_ENABLED inline RealType quantile(const logistic_distribution& dist, const RealType& p) { - BOOST_MATH_STD_USING RealType location = dist.location(); RealType scale = dist.scale(); @@ -220,13 +219,12 @@ namespace boost { namespace math { return policies::raise_overflow_error(function,"probability argument is 1, must be >0 and <1",Policy()); } - return location - scale * -logit(p, Policy()); + return location + scale * logit(p, Policy()); } // RealType quantile(const logistic_distribution& dist, const RealType& p) template BOOST_MATH_GPU_ENABLED inline RealType cdf(const complemented2_type, RealType>& c) { - BOOST_MATH_STD_USING RealType location = c.dist.location(); RealType scale = c.dist.scale(); RealType x = c.param; @@ -250,7 +248,7 @@ namespace boost { namespace math { { return result; } - RealType power = -(x - location) / scale; + RealType power = (location - x) / scale; return logistic_sigmoid(power, Policy()); } @@ -293,7 +291,6 @@ namespace boost { namespace math { template BOOST_MATH_GPU_ENABLED inline RealType quantile(const complemented2_type, RealType>& c) { - BOOST_MATH_STD_USING RealType scale = c.dist.scale(); RealType location = c.dist.location(); constexpr auto function = "boost::math::quantile(const complement(logistic_distribution<%1%>&), %1%)"; @@ -316,7 +313,7 @@ namespace boost { namespace math { return policies::raise_overflow_error(function,"probability argument is 0, but must be >0 and <1",Policy()); } - return location + scale * -logit(q, Policy()); + return location - scale * logit(q, Policy()); } template From b042910dba773e1268d2685aba13cc4198992029 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 14 Aug 2025 09:57:43 +0200 Subject: [PATCH 18/23] Make power a promoted real type --- include/boost/math/distributions/logistic.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/boost/math/distributions/logistic.hpp b/include/boost/math/distributions/logistic.hpp index 1f8cb46fc3..f67d00cb87 100644 --- a/include/boost/math/distributions/logistic.hpp +++ b/include/boost/math/distributions/logistic.hpp @@ -147,7 +147,8 @@ namespace boost { namespace math { return result; } - RealType power = (x - location) / scale; + using promoted_real_type = typename policies::evaluation::type; + promoted_real_type power = (static_cast(x) - static_cast(location)) / static_cast(scale); return logistic_sigmoid(power, Policy()); } @@ -248,7 +249,9 @@ namespace boost { namespace math { { return result; } - RealType power = (location - x) / scale; + + using promoted_real_type = typename policies::evaluation::type; + promoted_real_type power = (static_cast(location) - static_cast(x)) / static_cast(scale); return logistic_sigmoid(power, Policy()); } From c49115c1fcb1ee6e64d7eefbac99205156f083ba Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 14 Aug 2025 10:39:00 +0200 Subject: [PATCH 19/23] Add make_forwarding_policy and make_forwarding_policy_t --- include/boost/math/policies/policy.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/boost/math/policies/policy.hpp b/include/boost/math/policies/policy.hpp index bec8886408..45462e7ccf 100644 --- a/include/boost/math/policies/policy.hpp +++ b/include/boost/math/policies/policy.hpp @@ -1003,6 +1003,22 @@ struct is_noexcept_error_policy && (t8::value != throw_on_error) && (t8::value != user_error)); }; +// Generate a forwarding policy to stop further promotion from occurring +// For example if a special function for float promotes to double, we don't want the next +// function in the call chain to then promote to long double +template +using make_forwarding_policy = + typename policies::normalise< + Policy, + policies::promote_float, + policies::promote_double, + policies::discrete_quantile<>, + policies::assert_undefined<> + >::type; + +template +using make_forwarding_policy_t = typename make_forwarding_policy::type; + }}} // namespaces #endif // BOOST_MATH_POLICY_HPP From dc23aa3363166bc5a606e8d7686997cb8bd688af Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 14 Aug 2025 10:39:13 +0200 Subject: [PATCH 20/23] Disable further promotion with forwarding policy --- include/boost/math/distributions/logistic.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/math/distributions/logistic.hpp b/include/boost/math/distributions/logistic.hpp index f67d00cb87..bc2dd1ff5a 100644 --- a/include/boost/math/distributions/logistic.hpp +++ b/include/boost/math/distributions/logistic.hpp @@ -149,7 +149,7 @@ namespace boost { namespace math { using promoted_real_type = typename policies::evaluation::type; promoted_real_type power = (static_cast(x) - static_cast(location)) / static_cast(scale); - return logistic_sigmoid(power, Policy()); + return logistic_sigmoid(power, policies::make_forwarding_policy_t()); } template @@ -252,7 +252,7 @@ namespace boost { namespace math { using promoted_real_type = typename policies::evaluation::type; promoted_real_type power = (static_cast(location) - static_cast(x)) / static_cast(scale); - return logistic_sigmoid(power, Policy()); + return logistic_sigmoid(power, policies::make_forwarding_policy_t()); } template From d87ce894eb929d4920fdf0fd4ca363f46cce2550 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 14 Aug 2025 10:43:20 +0200 Subject: [PATCH 21/23] Check validity of initial policy --- include/boost/math/policies/policy.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/boost/math/policies/policy.hpp b/include/boost/math/policies/policy.hpp index 45462e7ccf..d6d944af54 100644 --- a/include/boost/math/policies/policy.hpp +++ b/include/boost/math/policies/policy.hpp @@ -1007,7 +1007,11 @@ struct is_noexcept_error_policy // For example if a special function for float promotes to double, we don't want the next // function in the call chain to then promote to long double template -using make_forwarding_policy = +struct make_forwarding_policy +{ + static_assert(detail::is_valid_policy::value, "Policy must be valid to make a forwarding policy"); + + using type = typename policies::normalise< Policy, policies::promote_float, @@ -1015,6 +1019,7 @@ using make_forwarding_policy = policies::discrete_quantile<>, policies::assert_undefined<> >::type; +}; template using make_forwarding_policy_t = typename make_forwarding_policy::type; From 067c6e7a5e1c8270eafd197f62d55cad7c0e0086 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 14 Aug 2025 11:14:35 +0200 Subject: [PATCH 22/23] Add documentation --- doc/math.qbk | 9 +++++++++ doc/sf/logistic_sigmoid.qbk | 30 ++++++++++++++++++++++++++++++ doc/sf/logit.qbk | 30 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 doc/sf/logistic_sigmoid.qbk create mode 100644 doc/sf/logit.qbk diff --git a/doc/math.qbk b/doc/math.qbk index 385c93a5e8..d56fef2626 100644 --- a/doc/math.qbk +++ b/doc/math.qbk @@ -339,6 +339,10 @@ and use the function's name as the link text.] [def __hermite [link math_toolkit.sf_poly.hermite hermite]] [def __cardinal_b_splines [link math_toolkit.sf_poly.cardinal_b_splines cardinal_b_splines]] +[/logistic functions] +[def __logit [link math_toolkit.logistic.logit logit]] +[def __logistic_sigmoid [link math_toolkit.logistic.logistic_sigmoid logistic_sigmoid]] + [/Misc] [def __expint [link math_toolkit.expint.expint_i expint]] [def __spherical_harmonic [link math_toolkit.sf_poly.sph_harm spherical_harmonic]] @@ -666,6 +670,11 @@ and as a CD ISBN 0-9504833-2-X 978-0-9504833-2-0, Classification 519.2-dc22. [include sf/jacobi.qbk] [endsect] [/section:sf_poly Polynomials] +[section:logistic Logistic Functions] +[include sf/logit.qbk] +[include sf/logistic_sigmoid.qbk] +[endsect] [/section:logistic Logistic Functions] + [section:bessel Bessel Functions] [include sf/bessel_introduction.qbk] [include sf/bessel_jy.qbk] diff --git a/doc/sf/logistic_sigmoid.qbk b/doc/sf/logistic_sigmoid.qbk new file mode 100644 index 0000000000..bf1c6c37fd --- /dev/null +++ b/doc/sf/logistic_sigmoid.qbk @@ -0,0 +1,30 @@ +[/ + Copyright Matt Borland 2025 + 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:logistic_sigmoid logistic_sigmoid] + +[h4 Synopsis] + + #include + + namespace boost { namespace math { + + template + RealType logistic_sigmoid(RealType x, const Policy&); + + template + RealType logistic_sigmoid(RealType x) + + }} // namespaces + +[h4 Description] + +Returns the [@https://en.wikipedia.org/wiki/Logistic_function logistic sigmoid function] +This function is broadly useful, and is used to compute the CDF of the logistic distribution. +It is also sometimes referred to as expit as it is the inverse of the logit function. + +[endsect] diff --git a/doc/sf/logit.qbk b/doc/sf/logit.qbk new file mode 100644 index 0000000000..7daebe667f --- /dev/null +++ b/doc/sf/logit.qbk @@ -0,0 +1,30 @@ +[/ + Copyright Matt Borland 2025 + 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:logit logit] + +[h4 Synopsis] + + #include + + namespace boost { namespace math { + + template + RealType logit(RealType x, const Policy&); + + template + RealType logit(RealType x) + + }} // namespaces + +[h4 Description] + +Returns the [@https://en.wikipedia.org/wiki/Logit logit function] +This function is broadly useful, and is used to compute the Quantile of the logistic distribution. +The inverse of this function is the logistic_sigmoid. + +[endsect] From f87d826adf8c69bf010eced2de9456b9503ffffc Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 14 Aug 2025 12:39:56 +0200 Subject: [PATCH 23/23] Remove incorrect assertion --- include/boost/math/policies/policy.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/boost/math/policies/policy.hpp b/include/boost/math/policies/policy.hpp index d6d944af54..5fb41bcd1a 100644 --- a/include/boost/math/policies/policy.hpp +++ b/include/boost/math/policies/policy.hpp @@ -1009,8 +1009,6 @@ struct is_noexcept_error_policy template struct make_forwarding_policy { - static_assert(detail::is_valid_policy::value, "Policy must be valid to make a forwarding policy"); - using type = typename policies::normalise< Policy,