test_lambert_w_integrals_float128.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright Paul A. Bristow 2016, 2017, 2018.
  2. // Copyright John Maddock 2016.
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt
  6. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. // test_lambert_w_integrals.cpp
  8. //! \brief quadrature tests that cover the whole range of the Lambert W0 function.
  9. #include <boost/config.hpp> // for BOOST_MSVC definition etc.
  10. #include <boost/version.hpp> // for BOOST_MSVC versions.
  11. #ifdef BOOST_HAS_FLOAT128
  12. // Boost macros
  13. #define BOOST_TEST_MAIN
  14. #define BOOST_LIB_DIAGNOSTIC "on" // Report library file details.
  15. #include <boost/test/included/unit_test.hpp> // Boost.Test
  16. #include <boost/test/tools/floating_point_comparison.hpp>
  17. #include <boost/array.hpp>
  18. #include <boost/lexical_cast.hpp>
  19. #include <boost/type_traits/is_constructible.hpp>
  20. #include <boost/multiprecision/float128.hpp>
  21. #include <boost/math/special_functions/fpclassify.hpp> // isnan, ifinite.
  22. #include <boost/math/special_functions/next.hpp> // float_next, float_prior
  23. using boost::math::float_next;
  24. using boost::math::float_prior;
  25. #include <boost/math/special_functions/ulp.hpp> // ulp
  26. #include <boost/math/tools/test_value.hpp> // for create_test_value and macro BOOST_MATH_TEST_VALUE.
  27. #include <boost/math/policies/policy.hpp>
  28. using boost::math::policies::digits2;
  29. using boost::math::policies::digits10;
  30. #include <boost/math/special_functions/lambert_w.hpp> // For Lambert W lambert_w function.
  31. using boost::math::lambert_wm1;
  32. using boost::math::lambert_w0;
  33. #include <limits>
  34. #include <cmath>
  35. #include <typeinfo>
  36. #include <iostream>
  37. #include <type_traits>
  38. #include <exception>
  39. std::string show_versions(void);
  40. // Added code and test for Integral of the Lambert W function: by Nick Thompson.
  41. // https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals
  42. #include <boost/math/constants/constants.hpp> // for integral tests.
  43. #include <boost/math/quadrature/tanh_sinh.hpp> // for integral tests.
  44. #include <boost/math/quadrature/exp_sinh.hpp> // for integral tests.
  45. using boost::math::policies::policy;
  46. using boost::math::policies::make_policy;
  47. // using statements needed for changing error handling policy.
  48. using boost::math::policies::evaluation_error;
  49. using boost::math::policies::domain_error;
  50. using boost::math::policies::overflow_error;
  51. using boost::math::policies::ignore_error;
  52. using boost::math::policies::throw_on_error;
  53. typedef policy<
  54. domain_error<throw_on_error>,
  55. overflow_error<ignore_error>
  56. > no_throw_policy;
  57. // Assumes that function has a throw policy, for example:
  58. // NOT lambert_w0<T>(1 / (x * x), no_throw_policy());
  59. // Error in function boost::math::quadrature::exp_sinh<double>::integrate:
  60. // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf.
  61. // Please ensure your function evaluates to a finite number of its entire domain.
  62. template <typename T>
  63. T debug_integration_proc(T x)
  64. {
  65. T result; // warning C4701: potentially uninitialized local variable 'result' used
  66. // T result = 0 ; // But result may not be assigned below?
  67. try
  68. {
  69. // Assign function call to result in here...
  70. if (x <= sqrt(boost::math::tools::min_value<T>()) )
  71. {
  72. result = 0;
  73. }
  74. else
  75. {
  76. result = lambert_w0<T>(1 / (x * x));
  77. }
  78. // result = lambert_w0<T>(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is:
  79. // Error in function boost::math::quadrature::exp_sinh<double>::integrate:
  80. // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf.
  81. // Please ensure your function evaluates to a finite number of its entire domain.
  82. } // try
  83. catch (const std::exception& e)
  84. {
  85. std::cout << "Exception " << e.what() << std::endl;
  86. // set breakpoint here:
  87. std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl;
  88. if (!std::isfinite(result))
  89. {
  90. // set breakpoint here:
  91. std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl;
  92. }
  93. if (std::isnan(result))
  94. {
  95. // set breakpoint here:
  96. std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl;
  97. }
  98. } // catch
  99. return result;
  100. } // T debug_integration_proc(T x)
  101. template<class Real>
  102. void test_integrals()
  103. {
  104. // Integral of the Lambert W function:
  105. // https://en.wikipedia.org/wiki/Lambert_W_function
  106. using boost::math::quadrature::tanh_sinh;
  107. using boost::math::quadrature::exp_sinh;
  108. // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html
  109. using std::sqrt;
  110. std::cout << "Integration of type " << typeid(Real).name() << std::endl;
  111. Real tol = std::numeric_limits<Real>::epsilon();
  112. { // // Integrate for function lambert_W0(z);
  113. tanh_sinh<Real> ts;
  114. Real a = 0;
  115. Real b = boost::math::constants::e<Real>();
  116. auto f = [](Real z)->Real
  117. {
  118. return lambert_w0<Real>(z);
  119. };
  120. Real z = ts.integrate(f, a, b); // OK without any decltype(f)
  121. BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e<Real>() - 1, tol);
  122. }
  123. {
  124. // Integrate for function lambert_W0(z/(z sqrt(z)).
  125. exp_sinh<Real> es;
  126. auto f = [](Real z)->Real
  127. {
  128. return lambert_w0<Real>(z)/(z * sqrt(z));
  129. };
  130. Real z = es.integrate(f); // OK
  131. BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi<Real>(), tol);
  132. }
  133. {
  134. // Integrate for function lambert_W0(1/z^2).
  135. exp_sinh<Real> es;
  136. //const Real sqrt_min = sqrt(boost::math::tools::min_value<Real>()); // 1.08420217e-19 fo 32-bit float.
  137. // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified
  138. auto f = [](Real z)->Real
  139. {
  140. if (z <= sqrt(boost::math::tools::min_value<Real>()) )
  141. { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter.
  142. return static_cast<Real>(0);
  143. }
  144. else
  145. {
  146. return lambert_w0<Real>(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen.
  147. }
  148. };
  149. Real z = es.integrate(f);
  150. BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi<Real>(), tol);
  151. }
  152. } // template<class Real> void test_integrals()
  153. BOOST_AUTO_TEST_CASE( integrals )
  154. {
  155. std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl;
  156. BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals.");
  157. try
  158. {
  159. // using statements needed to change precision policy.
  160. using boost::math::policies::policy;
  161. using boost::math::policies::make_policy;
  162. using boost::math::policies::precision;
  163. using boost::math::policies::digits2;
  164. using boost::math::policies::digits10;
  165. // using statements needed for changing error handling policy.
  166. using boost::math::policies::evaluation_error;
  167. using boost::math::policies::domain_error;
  168. using boost::math::policies::overflow_error;
  169. using boost::math::policies::ignore_error;
  170. using boost::math::policies::throw_on_error;
  171. /*
  172. typedef policy<
  173. domain_error<throw_on_error>,
  174. overflow_error<ignore_error>
  175. > no_throw_policy;
  176. // Experiment with better diagnostics.
  177. typedef float Real;
  178. Real inf = std::numeric_limits<Real>::infinity();
  179. Real max = (std::numeric_limits<Real>::max)();
  180. std::cout.precision(std::numeric_limits<Real>::max_digits10);
  181. //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308
  182. std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf
  183. std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227
  184. //std::cout << lambert_w0(inf) << std::endl; // inf - will throw.
  185. std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0
  186. std::cout << "lambert_w0(std::numeric_limits<Real>::denorm_min()) = " << lambert_w0(std::numeric_limits<Real>::denorm_min()) << std::endl; // 4.94066e-324
  187. std::cout << "lambert_w0(std::numeric_limits<Real>::min()) = " << lambert_w0((std::numeric_limits<Real>::min)()) << std::endl; // 2.22507e-308
  188. // Approximate the largest lambert_w you can get for type T?
  189. float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits<float>::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162.
  190. std::cout << "w max_f " << max_w_f << std::endl; // 84.2879
  191. Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits<Real>::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162.
  192. std::cout << "w max " << max_w << std::endl; // 703.227
  193. std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; //
  194. std::cout << "test integral 1/z^2" << std::endl;
  195. std::cout << "ULP = " << boost::math::ulp(1., policy<digits2<> >()) << std::endl; // ULP = 2.2204460492503131e-16
  196. std::cout << "ULP = " << boost::math::ulp(1e-10, policy<digits2<> >()) << std::endl; // ULP = 2.2204460492503131e-16
  197. std::cout << "ULP = " << boost::math::ulp(1., policy<digits2<11> >()) << std::endl; // ULP = 2.2204460492503131e-16
  198. std::cout << "epsilon = " << std::numeric_limits<Real>::epsilon() << std::endl; //
  199. std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value<float>() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19
  200. std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value<float>() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19
  201. // Demo debug version.
  202. Real tol = std::numeric_limits<Real>::epsilon();
  203. Real x;
  204. {
  205. using boost::math::quadrature::exp_sinh;
  206. exp_sinh<Real> es;
  207. // Function to be integrated, lambert_w0(1/z^2).
  208. //auto f = [](Real z)->Real
  209. //{ // Naive - no protection against underflow and subsequent divide by zero.
  210. // return lambert_w0<Real>(1 / (z * z));
  211. //};
  212. // Diagnostic is:
  213. // Error in function boost::math::lambert_w0<Real>: Expected a finite value but got inf
  214. auto f = [](Real z)->Real
  215. { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things.
  216. return debug_integration_proc(z);
  217. };
  218. // Exception Error in function boost::math::lambert_w0<double>: Expected a finite value but got inf.
  219. // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163.
  220. // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23.
  221. x = es.integrate(f);
  222. std::cout << "es.integrate(f) = " << x << std::endl;
  223. BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi<Real>(), tol);
  224. // root_two_pi<double = 2.506628274631000502
  225. }
  226. */
  227. test_integrals<boost::multiprecision::float128>();
  228. }
  229. catch (std::exception& ex)
  230. {
  231. std::cout << ex.what() << std::endl;
  232. }
  233. }
  234. #else
  235. int main() { return 0; }
  236. #endif