// Copyright Paul A. Bristow 2016, 2017, 2018. // Copyright John Maddock 2016. // 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) // test_lambert_w_integrals.cpp //! \brief quadrature tests that cover the whole range of the Lambert W0 function. #include // for BOOST_MSVC definition etc. #include // for BOOST_MSVC versions. #ifdef BOOST_HAS_FLOAT128 // Boost macros #define BOOST_TEST_MAIN #define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. #include // Boost.Test #include #include #include #include #include #include // isnan, ifinite. #include // float_next, float_prior using boost::math::float_next; using boost::math::float_prior; #include // ulp #include // for create_test_value and macro BOOST_MATH_TEST_VALUE. #include using boost::math::policies::digits2; using boost::math::policies::digits10; #include // For Lambert W lambert_w function. using boost::math::lambert_wm1; using boost::math::lambert_w0; #include #include #include #include #include #include std::string show_versions(void); // Added code and test for Integral of the Lambert W function: by Nick Thompson. // https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals #include // for integral tests. #include // for integral tests. #include // for integral tests. using boost::math::policies::policy; using boost::math::policies::make_policy; // using statements needed for changing error handling policy. using boost::math::policies::evaluation_error; using boost::math::policies::domain_error; using boost::math::policies::overflow_error; using boost::math::policies::ignore_error; using boost::math::policies::throw_on_error; typedef policy< domain_error, overflow_error > no_throw_policy; // Assumes that function has a throw policy, for example: // NOT lambert_w0(1 / (x * x), no_throw_policy()); // Error in function boost::math::quadrature::exp_sinh::integrate: // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. // Please ensure your function evaluates to a finite number of its entire domain. template T debug_integration_proc(T x) { T result; // warning C4701: potentially uninitialized local variable 'result' used // T result = 0 ; // But result may not be assigned below? try { // Assign function call to result in here... if (x <= sqrt(boost::math::tools::min_value()) ) { result = 0; } else { result = lambert_w0(1 / (x * x)); } // result = lambert_w0(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is: // Error in function boost::math::quadrature::exp_sinh::integrate: // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. // Please ensure your function evaluates to a finite number of its entire domain. } // try catch (const std::exception& e) { std::cout << "Exception " << e.what() << std::endl; // set breakpoint here: std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl; if (!std::isfinite(result)) { // set breakpoint here: std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; } if (std::isnan(result)) { // set breakpoint here: std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; } } // catch return result; } // T debug_integration_proc(T x) template void test_integrals() { // Integral of the Lambert W function: // https://en.wikipedia.org/wiki/Lambert_W_function using boost::math::quadrature::tanh_sinh; using boost::math::quadrature::exp_sinh; // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html using std::sqrt; std::cout << "Integration of type " << typeid(Real).name() << std::endl; Real tol = std::numeric_limits::epsilon(); { // // Integrate for function lambert_W0(z); tanh_sinh ts; Real a = 0; Real b = boost::math::constants::e(); auto f = [](Real z)->Real { return lambert_w0(z); }; Real z = ts.integrate(f, a, b); // OK without any decltype(f) BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e() - 1, tol); } { // Integrate for function lambert_W0(z/(z sqrt(z)). exp_sinh es; auto f = [](Real z)->Real { return lambert_w0(z)/(z * sqrt(z)); }; Real z = es.integrate(f); // OK BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi(), tol); } { // Integrate for function lambert_W0(1/z^2). exp_sinh es; //const Real sqrt_min = sqrt(boost::math::tools::min_value()); // 1.08420217e-19 fo 32-bit float. // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified auto f = [](Real z)->Real { if (z <= sqrt(boost::math::tools::min_value()) ) { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter. return static_cast(0); } else { return lambert_w0(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen. } }; Real z = es.integrate(f); BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi(), tol); } } // template void test_integrals() BOOST_AUTO_TEST_CASE( integrals ) { std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl; BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals."); try { // using statements needed to change precision policy. using boost::math::policies::policy; using boost::math::policies::make_policy; using boost::math::policies::precision; using boost::math::policies::digits2; using boost::math::policies::digits10; // using statements needed for changing error handling policy. using boost::math::policies::evaluation_error; using boost::math::policies::domain_error; using boost::math::policies::overflow_error; using boost::math::policies::ignore_error; using boost::math::policies::throw_on_error; /* typedef policy< domain_error, overflow_error > no_throw_policy; // Experiment with better diagnostics. typedef float Real; Real inf = std::numeric_limits::infinity(); Real max = (std::numeric_limits::max)(); std::cout.precision(std::numeric_limits::max_digits10); //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308 std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227 //std::cout << lambert_w0(inf) << std::endl; // inf - will throw. std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0 std::cout << "lambert_w0(std::numeric_limits::denorm_min()) = " << lambert_w0(std::numeric_limits::denorm_min()) << std::endl; // 4.94066e-324 std::cout << "lambert_w0(std::numeric_limits::min()) = " << lambert_w0((std::numeric_limits::min)()) << std::endl; // 2.22507e-308 // Approximate the largest lambert_w you can get for type T? float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. std::cout << "w max_f " << max_w_f << std::endl; // 84.2879 Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. std::cout << "w max " << max_w << std::endl; // 703.227 std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; // std::cout << "test integral 1/z^2" << std::endl; std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 std::cout << "ULP = " << boost::math::ulp(1e-10, policy >()) << std::endl; // ULP = 2.2204460492503131e-16 std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 std::cout << "epsilon = " << std::numeric_limits::epsilon() << std::endl; // std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19 std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19 // Demo debug version. Real tol = std::numeric_limits::epsilon(); Real x; { using boost::math::quadrature::exp_sinh; exp_sinh es; // Function to be integrated, lambert_w0(1/z^2). //auto f = [](Real z)->Real //{ // Naive - no protection against underflow and subsequent divide by zero. // return lambert_w0(1 / (z * z)); //}; // Diagnostic is: // Error in function boost::math::lambert_w0: Expected a finite value but got inf auto f = [](Real z)->Real { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things. return debug_integration_proc(z); }; // Exception Error in function boost::math::lambert_w0: Expected a finite value but got inf. // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163. // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23. x = es.integrate(f); std::cout << "es.integrate(f) = " << x << std::endl; BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi(), tol); // root_two_pi(); } catch (std::exception& ex) { std::cout << ex.what() << std::endl; } } #else int main() { return 0; } #endif