lambert_w_example.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright Paul A. Bristow 2016.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or
  4. // copy at http ://www.boost.org/LICENSE_1_0.txt).
  5. // Test that can build and run a simple example of Lambert W function,
  6. // using algorithm of Thomas Luu.
  7. // https://svn.boost.org/trac/boost/ticket/11027
  8. #include <boost/config.hpp> // for BOOST_PLATFORM, BOOST_COMPILER, BOOST_STDLIB ...
  9. #include <boost/version.hpp> // for BOOST_MSVC versions.
  10. #include <boost/cstdint.hpp>
  11. #include <boost/exception/exception.hpp> // boost::exception
  12. #include <boost/math/constants/constants.hpp> // For exp_minus_one == 3.67879441171442321595523770161460867e-01.
  13. #define BOOST_MATH_INSTRUMENT_LAMBERT_W // #define only for diagnostic output.
  14. // For lambert_w function.
  15. #include <boost/math/special_functions/lambert_w.hpp>
  16. #include <iostream>
  17. // using std::cout;
  18. // using std::endl;
  19. #include <exception>
  20. #include <stdexcept>
  21. #include <string>
  22. #include <limits> // For std::numeric_limits.
  23. //! Show information about build, architecture, address model, platform, ...
  24. std::string show_versions()
  25. {
  26. std::ostringstream message;
  27. message << "Program: " << __FILE__ << "\n";
  28. #ifdef __TIMESTAMP__
  29. message << __TIMESTAMP__;
  30. #endif
  31. message << "\nBuildInfo:\n" " Platform " << BOOST_PLATFORM;
  32. // http://stackoverflow.com/questions/1505582/determining-32-vs-64-bit-in-c
  33. #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
  34. #define IS64BIT 1
  35. message << ", 64-bit.";
  36. #else
  37. #define IS32BIT 1
  38. message << ", 32-bit.";
  39. #endif
  40. message << "\n Compiler " BOOST_COMPILER;
  41. #ifdef BOOST_MSC_VER
  42. #ifdef _MSC_FULL_VER
  43. message << "\n MSVC version " << BOOST_STRINGIZE(_MSC_FULL_VER) << ".";
  44. #endif
  45. #ifdef __WIN64
  46. mess age << "\n WIN64" << std::endl;
  47. #endif // __WIN64
  48. #ifdef _WIN32
  49. message << "\n WIN32" << std::endl;
  50. #endif // __WIN32
  51. #endif
  52. #ifdef __GNUC__
  53. //PRINT_MACRO(__GNUC__);
  54. //PRINT_MACRO(__GNUC_MINOR__);
  55. //PRINT_MACRO(__GNUC_PATCH__);
  56. std::cout << "GCC " << __VERSION__ << std::endl;
  57. //PRINT_MACRO(LONG_MAX);
  58. #endif // __GNUC__
  59. message << "\n STL " << BOOST_STDLIB;
  60. message << "\n Boost version " << BOOST_VERSION / 100000 << "." << BOOST_VERSION / 100 % 1000 << "." << BOOST_VERSION % 100;
  61. #ifdef BOOST_HAS_FLOAT128
  62. message << ", BOOST_HAS_FLOAT128" << std::endl;
  63. #endif
  64. message << std::endl;
  65. return message.str();
  66. } // std::string versions()
  67. int main()
  68. {
  69. try
  70. {
  71. //std::cout << "Lambert W example basic!" << std::endl;
  72. //std::cout << show_versions() << std::endl;
  73. //std::cout << exp(1) << std::endl; // 2.71828
  74. //std::cout << exp(-1) << std::endl; // 0.367879
  75. //std::cout << std::numeric_limits<double>::epsilon() / 2 << std::endl; // 1.11022e-16
  76. using namespace boost::math;
  77. using boost::math::constants::exp_minus_one;
  78. double x = 1.;
  79. double W1 = lambert_w(1.);
  80. // Note, NOT integer X, for example: lambert_w(1); or will get message like
  81. // error C2338: Must be floating-point, not integer type, for example W(1.), not W(1)!
  82. //
  83. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.567143
  84. // This 'golden ratio' for exponentials is http://mathworld.wolfram.com/OmegaConstant.html
  85. // since exp[-W(1)] = W(1)
  86. // A030178 Decimal expansion of LambertW(1): the solution to x*exp(x)
  87. // = 0.5671432904097838729999686622103555497538157871865125081351310792230457930866
  88. // http://oeis.org/A030178
  89. double expplogone = exp(-lambert_w(1.));
  90. if (expplogone != W1)
  91. {
  92. std::cout << expplogone << " " << W1 << std::endl; //
  93. }
  94. //[lambert_w_example_1
  95. x = 0.01;
  96. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.00990147
  97. //] [/lambert_w_example_1]
  98. x = -0.01;
  99. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // -0.0101015
  100. x = -0.1;
  101. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; //
  102. /**/
  103. for (double xd = 1.; xd < 1e20; xd *= 10)
  104. {
  105. // 1. 0.56714329040978387
  106. // 0.56714329040978384
  107. // 10 1.7455280027406994
  108. // 1.7455280027406994
  109. // 100 3.3856301402900502
  110. // 3.3856301402900502
  111. // 1000 5.2496028524015959
  112. // 5.249602852401596227126056319697306282521472386059592844451465483991362228320942832739693150854347718
  113. // 1e19 40.058769161984308
  114. // 40.05876916198431163898797971203180915622644925765346546858291325452428038208071849105889199253335063
  115. std::cout << "Lambert W (" << xd << ") = " << lambert_w(xd) << std::endl; //
  116. }
  117. //
  118. // Test near singularity.
  119. // http://www.wolframalpha.com/input/?i=N%5Blambert_w%5B-0.367879%5D,17%5D test value N[lambert_w[-0.367879],17]
  120. // -0.367879441171442321595523770161460867445811131031767834
  121. x = -0.367879; // < -exp(1) = -0.367879
  122. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // Lambert W (-0.36787900000000001) = -0.99845210378080340
  123. // -0.99845210378080340
  124. // -0.99845210378072726 N[lambert_w[-0.367879],17] wolfram so very close.
  125. x = -0.3678794; // expect -0.99952696660756813
  126. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  127. x = -0.36787944; // expect -0.99992019848408340
  128. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  129. x = -0.367879441; // -0.99996947070054883
  130. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  131. x = -0.36787944117; // -0.99999719977527159
  132. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  133. x = -0.367879441171; // -0.99999844928821992
  134. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  135. x = -exp_minus_one<double>() + std::numeric_limits<double>::epsilon();
  136. // Lambert W (-0.36787944117144211) = -0.99999996349975895
  137. // N[lambert_w[-0.36787944117144211],17] == -0.99999996608315303
  138. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  139. std::cout << " 1 - sqrt(eps) = " << static_cast<double>(1) - sqrt(std::numeric_limits<double>::epsilon()) << std::endl;
  140. x = -exp_minus_one<double>();
  141. // N[lambert_w[-0.36787944117144233],17] == -1.000000000000000 + 6.7595465843924897*10^-9i
  142. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0
  143. // At Singularity - 0.36787944117144233 == -0.36787944117144233 returned - 1.0000000000000000
  144. // Lambert W(-0.36787944117144233) = -1.0000000000000000
  145. x = (std::numeric_limits<double>::max)()/4;
  146. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // OK 702.023799146706
  147. x = (std::numeric_limits<double>::max)()/2;
  148. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; //
  149. x = (std::numeric_limits<double>::max)();
  150. std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; //
  151. // Error in function boost::math::log1p<double>(double): numeric overflow
  152. /* */
  153. }
  154. catch (std::exception& ex)
  155. {
  156. std::cout << ex.what() << std::endl;
  157. }
  158. } // int main()
  159. /*
  160. //[lambert_w_output_1
  161. Output:
  162. 1> example_basic.cpp
  163. 1> Generating code
  164. 1> All 237 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
  165. 1> Finished generating code
  166. 1> LambertW.vcxproj -> J:\Cpp\Misc\x64\Release\LambertW.exe
  167. 1> LambertW.vcxproj -> J:\Cpp\Misc\x64\Release\LambertW.pdb (Full PDB)
  168. 1> Lambert W example basic!
  169. 1> Platform: Win32
  170. 1> Compiler: Microsoft Visual C++ version 14.0
  171. 1> STL : Dinkumware standard library version 650
  172. 1> Boost : 1.63.0
  173. 1> _MSC_FULL_VER = 190024123
  174. 1> Win32
  175. 1> x64
  176. 1> (x64)
  177. 1> Iteration #0, w0 0.577547206058041, w1 = 0.567143616915443, difference = 0.0289944962755619, relative 0.018343835374856
  178. 1> Iteration #1, w0 0.567143616915443, w1 = 0.567143290409784, difference = 9.02208135089566e-07, relative 5.75702234328901e-07
  179. 1> Final 0.567143290409784 after 2 iterations, difference = 0
  180. 1> Iteration #0, w0 0.577547206058041, w1 = 0.567143616915443, difference = 0.0289944962755619, relative 0.018343835374856
  181. 1> Iteration #1, w0 0.567143616915443, w1 = 0.567143290409784, difference = 9.02208135089566e-07, relative 5.75702234328901e-07
  182. 1> Final 0.567143290409784 after 2 iterations, difference = 0
  183. 1> Lambert W (1) = 0.567143290409784
  184. 1> Iteration #0, w0 0.577547206058041, w1 = 0.567143616915443, difference = 0.0289944962755619, relative 0.018343835374856
  185. 1> Iteration #1, w0 0.567143616915443, w1 = 0.567143290409784, difference = 9.02208135089566e-07, relative 5.75702234328901e-07
  186. 1> Final 0.567143290409784 after 2 iterations, difference = 0
  187. 1> Iteration #0, w0 0.0099072820916067, w1 = 0.00990147384359511, difference = 5.92416060777624e-06, relative 0.000586604388734591
  188. 1> Final 0.00990147384359511 after 1 iterations, difference = 0
  189. 1> Lambert W (0.01) = 0.00990147384359511
  190. 1> Iteration #0, w0 -0.0101016472705154, w1 = -0.0101015271985388, difference = -1.17664437923951e-07, relative 1.18865171889748e-05
  191. 1> Final -0.0101015271985388 after 1 iterations, difference = 0
  192. 1> Lambert W (-0.01) = -0.0101015271985388
  193. 1> Iteration #0, w0 -0.111843322610692, w1 = -0.111832559158964, difference = -8.54817065376601e-06, relative 9.62461362694622e-05
  194. 1> Iteration #1, w0 -0.111832559158964, w1 = -0.111832559158963, difference = -5.68989300120393e-16, relative 6.43929354282591e-15
  195. 1> Final -0.111832559158963 after 2 iterations, difference = 0
  196. 1> Lambert W (-0.1) = -0.111832559158963
  197. 1> Iteration #0, w0 -0.998452103785573, w1 = -0.998452103780803, difference = -2.72004641033163e-15, relative 4.77662354114727e-12
  198. 1> Final -0.998452103780803 after 1 iterations, difference = 0
  199. 1> Lambert W (-0.367879) = -0.998452103780803
  200. //] [/lambert_w_output_1]
  201. */