You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
11KB

  1. /*
  2. Implementation of the Lambert W function
  3. Copyright (C) 2011 Darko Veberic, darko.veberic@ijs.si
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include <cmath>
  16. #include "Horner.h"
  17. using namespace std;
  18. // fork macro to keep the tree balanced
  19. #define Y2(d1, c12, d2) \
  20. ((c12) ? (d1) : (d2))
  21. #define Y3(d1, c12, d2, c23, d3) \
  22. Y2(Y2(d1, c12, d2), c23, d3)
  23. #define Y4(d1, c12, d2, c23, d3, c34, d4) \
  24. Y3(d1, c12, d2, c23, Y2(d3, c34, d4))
  25. #define Y5(d1, c12, d2, c23, d3, c34, d4, c45, d5) \
  26. Y4(Y2(d1, c12, d2), c23, d3, c34, d4, c45, d5)
  27. #define Y6(d1, c12, d2, c23, d3, c34, d4, c45, d5, c56, d6) \
  28. Y5(d1, c12, d2, c23, d3, c34, d4, c45, Y2(d5, c56, d6))
  29. #define Y7(d1, c12, d2, c23, d3, c34, d4, c45, d5, c56, d6, c67, d7) \
  30. Y6(d1, c12, d2, c23, Y2(d3, c34, d4), c45, d5, c56, d6, c67, d7)
  31. namespace utl {
  32. class BranchPoint { };
  33. HORNER_COEFF(BranchPoint, 0, -1);
  34. HORNER_COEFF(BranchPoint, 1, 1);
  35. HORNER_COEFF(BranchPoint, 2, -0.333333333333333333e0);
  36. HORNER_COEFF(BranchPoint, 3, 0.152777777777777777e0);
  37. HORNER_COEFF(BranchPoint, 4, -0.79629629629629630e-1);
  38. HORNER_COEFF(BranchPoint, 5, 0.44502314814814814e-1);
  39. HORNER_COEFF(BranchPoint, 6, -0.25984714873603760e-1);
  40. HORNER_COEFF(BranchPoint, 7, 0.15635632532333920e-1);
  41. HORNER_COEFF(BranchPoint, 8, -0.96168920242994320e-2);
  42. HORNER_COEFF(BranchPoint, 9, 0.60145432529561180e-2);
  43. HORNER_COEFF(BranchPoint, 10, -0.38112980348919993e-2);
  44. HORNER_COEFF(BranchPoint, 11, 0.24408779911439826e-2);
  45. HORNER_COEFF(BranchPoint, 12, -0.15769303446867841e-2);
  46. HORNER_COEFF(BranchPoint, 13, 0.10262633205076071e-2);
  47. HORNER_COEFF(BranchPoint, 14, -0.67206163115613620e-3);
  48. HORNER_COEFF(BranchPoint, 15, 0.44247306181462090e-3);
  49. HORNER_COEFF(BranchPoint, 16, -0.29267722472962746e-3);
  50. HORNER_COEFF(BranchPoint, 17, 0.19438727605453930e-3);
  51. HORNER_COEFF(BranchPoint, 18, -0.12957426685274883e-3);
  52. HORNER_COEFF(BranchPoint, 19, 0.86650358052081260e-4);
  53. template<unsigned int order>
  54. class AsymptoticPolynomialB { };
  55. //HORNER_COEFF(AsymptoticPolynomialB<0>, 0, 0);
  56. //HORNER_COEFF(AsymptoticPolynomialB<0>, 1, -1);
  57. //HORNER_COEFF(AsymptoticPolynomialB<1>, 0, 0);
  58. //HORNER_COEFF(AsymptoticPolynomialB<1>, 1, 1);
  59. HORNER_COEFF(AsymptoticPolynomialB<2>, 0, 0);
  60. HORNER_COEFF(AsymptoticPolynomialB<2>, 1, -1);
  61. HORNER_COEFF(AsymptoticPolynomialB<2>, 2, 1./2);
  62. HORNER_COEFF(AsymptoticPolynomialB<3>, 0, 0);
  63. HORNER_COEFF(AsymptoticPolynomialB<3>, 1, 1);
  64. HORNER_COEFF(AsymptoticPolynomialB<3>, 2, -3./2);
  65. HORNER_COEFF(AsymptoticPolynomialB<3>, 3, 1./3);
  66. HORNER_COEFF(AsymptoticPolynomialB<4>, 0, 0);
  67. HORNER_COEFF(AsymptoticPolynomialB<4>, 1, -1);
  68. HORNER_COEFF(AsymptoticPolynomialB<4>, 2, 3);
  69. HORNER_COEFF(AsymptoticPolynomialB<4>, 3, -11./6);
  70. HORNER_COEFF(AsymptoticPolynomialB<4>, 4, 1./4);
  71. HORNER_COEFF(AsymptoticPolynomialB<5>, 0, 0);
  72. HORNER_COEFF(AsymptoticPolynomialB<5>, 1, 1);
  73. HORNER_COEFF(AsymptoticPolynomialB<5>, 2, -5);
  74. HORNER_COEFF(AsymptoticPolynomialB<5>, 3, 35./6);
  75. HORNER_COEFF(AsymptoticPolynomialB<5>, 4, -25./12);
  76. HORNER_COEFF(AsymptoticPolynomialB<5>, 5, 1./5);
  77. class AsymptoticPolynomialA { };
  78. //HORNER_COEFF2(AsymptoticPolynomialA, 0, (Horner<Float, AsymptoticPolynomialB<0>, 1>::Eval(y)));
  79. HORNER_COEFF2(AsymptoticPolynomialA, 0, -y);
  80. //HORNER_COEFF2(AsymptoticPolynomialA, 1, (Horner<Float, AsymptoticPolynomialB<1>, 1>::Eval(y)));
  81. HORNER_COEFF2(AsymptoticPolynomialA, 1, y);
  82. HORNER_COEFF2(AsymptoticPolynomialA, 2, (Horner<Float, AsymptoticPolynomialB<2>, 2>::Eval(y)));
  83. HORNER_COEFF2(AsymptoticPolynomialA, 3, (Horner<Float, AsymptoticPolynomialB<3>, 3>::Eval(y)));
  84. HORNER_COEFF2(AsymptoticPolynomialA, 4, (Horner<Float, AsymptoticPolynomialB<4>, 4>::Eval(y)));
  85. HORNER_COEFF2(AsymptoticPolynomialA, 5, (Horner<Float, AsymptoticPolynomialB<5>, 5>::Eval(y)));
  86. template<typename Float, int order>
  87. inline
  88. Float
  89. AsymptoticExpansionImpl(const Float a, const Float b)
  90. {
  91. return a + Horner<Float, AsymptoticPolynomialA, order>::Eval(1/a, b);
  92. }
  93. template<typename Float, int branch, int order>
  94. struct LogRecursionImpl {
  95. enum { eSign = 2*branch + 1 };
  96. static Float Step(const Float logsx)
  97. { return logsx - log(eSign * LogRecursionImpl<Float, branch, order-1>::Step(logsx)); }
  98. };
  99. template<typename Float, int branch>
  100. struct LogRecursionImpl<Float, branch, 0> {
  101. static Float Step(const Float logsx) { return logsx; }
  102. };
  103. template<typename Float, int branch>
  104. class Branch {
  105. public:
  106. template<int order>
  107. static Float BranchPointExpansion(const Float x)
  108. { return Horner<Float, BranchPoint, order>::Eval(eSign * sqrt(2*(Float(M_E)*x+1))); }
  109. // Asymptotic expansion: Corless et al. 1996, de Bruijn (1981)
  110. template<int order>
  111. static
  112. Float
  113. AsymptoticExpansion(const Float x)
  114. {
  115. const Float logsx = log(eSign * x);
  116. const Float logslogsx = log(eSign * logsx);
  117. return AsymptoticExpansionImpl<Float, order>(logsx, logslogsx);
  118. }
  119. // Logarithmic recursion
  120. template<int order>
  121. static Float LogRecursion(const Float x)
  122. { return LogRecursionImpl<Float, branch, order>::Step(log(eSign * x)); }
  123. private:
  124. enum { eSign = 2*branch + 1 };
  125. };
  126. // iterations
  127. template<typename Float>
  128. inline
  129. Float
  130. HalleyStep(const Float x, const Float w)
  131. {
  132. const Float ew = exp(w);
  133. const Float wew = w * ew;
  134. const Float wewx = wew - x;
  135. const Float w1 = w + 1;
  136. return w - wewx / (ew * w1 - (w + 2) * wewx/(2*w1));
  137. }
  138. template<typename Float>
  139. inline
  140. Float
  141. FritschStep(const Float x, const Float w)
  142. {
  143. const Float z = log(x/w) - w;
  144. const Float w1 = w + 1;
  145. const Float q = 2 * w1 * (w1 + Float(2./3)*z);
  146. const Float eps = z / w1 * (q - z) / (q - 2*z);
  147. return w * (1 + eps);
  148. }
  149. template<typename Float>
  150. inline
  151. Float
  152. SchroederStep(const Float x, const Float w)
  153. {
  154. const Float y = x * exp(-w);
  155. const Float f0 = w - y;
  156. const Float f1 = 1 + y;
  157. const Float f00 = f0*f0;
  158. const Float f11 = f1*f1;
  159. const Float f0y = f0*y;
  160. return w - 4*f0*(6*f1*(f11 + f0y) + f00*y) / (f11*(24*f11 + 36*f0y) + f00*(6*y*y + 8*f1*y + f0y));
  161. }
  162. template<
  163. typename Float,
  164. double IterationStep(const Float x, const Float w)
  165. >
  166. struct Iterator {
  167. template<int n, class = void>
  168. struct Depth {
  169. static Float Recurse(const Float x, Float w)
  170. { return Depth<n-1>::Recurse(x, IterationStep(x, w)); }
  171. };
  172. // stop condition
  173. template<class T>
  174. struct Depth<1, T> {
  175. static Float Recurse(const Float x, const Float w) { return IterationStep(x, w); }
  176. };
  177. // identity
  178. template<class T>
  179. struct Depth<0, T> {
  180. static Float Recurse(const Float x, const Float w) { return w; }
  181. };
  182. };
  183. // Rational approximations
  184. template<typename Float, int branch, int n>
  185. struct Pade {
  186. static inline Float Approximation(const Float x);
  187. };
  188. template<typename Float>
  189. struct Pade<Float, 0, 1> {
  190. static inline Float Approximation(const Float x)
  191. { return x * HORNER4(Float, x, 0.07066247420543414, 2.4326814530577687, 6.39672835731526, 4.663365025836821, 0.99999908757381) /
  192. HORNER4(Float, x, 1.2906660139511692, 7.164571775410987, 10.559985088953114, 5.66336307375819, 1); }
  193. };
  194. template<typename Float>
  195. struct Pade<Float, 0, 2> {
  196. static inline Float Approximation(const Float x)
  197. { const Float y = log(Float(0.5)*x) - 2;
  198. return 2 + y * HORNER3(Float, y, 0.00006979269679670452, 0.017110368846615806, 0.19338607770900237, 0.6666648896499793) /
  199. HORNER2(Float, y, 0.0188060684652668, 0.23451269827133317, 1); }
  200. };
  201. template<typename Float>
  202. struct Pade<Float, -1, 4> {
  203. static inline Float Approximation(const Float x)
  204. { return HORNER4(Float, x, -2793.4565508841197, -1987.3632221106518, 385.7992853617571, 277.2362778379572, -7.840776922133643) /
  205. HORNER4(Float, x, 280.6156995997829, 941.9414019982657, 190.64429338894644, -63.93540494358966, 1); }
  206. };
  207. template<typename Float>
  208. struct Pade<Float, -1, 5> {
  209. static inline Float Approximation(const Float x)
  210. { const Float y = log(-x);
  211. return -exp(
  212. HORNER3(Float, y, 0.16415668298255184, -3.334873920301941, 2.4831415860003747, 4.173424474574879) /
  213. HORNER3(Float, y, 0.031239411487374164, -1.2961659693400076, 4.517178492772906, 1)
  214. ); }
  215. };
  216. template<typename Float>
  217. struct Pade<Float, -1, 6> {
  218. static inline Float Approximation(const Float x)
  219. { const Float y = log(-x);
  220. return -exp(
  221. HORNER4(Float, y, 0.000026987243254533254, -0.007692106448267341, 0.28793461719300206, -1.5267058884647018, -0.5370669268991288) /
  222. HORNER4(Float, y, 3.6006502104930343e-6, -0.0015552463555591487, 0.08801194682489769, -0.8973922357575583, 1)
  223. ); }
  224. };
  225. template<typename Float>
  226. struct Pade<Float, -1, 7> {
  227. static inline Float Approximation(const Float x)
  228. { return -1 - sqrt(
  229. HORNER4(Float, x, 988.0070769375508, 1619.8111957356814, 989.2017745708083, 266.9332506485452, 26.875022558546036) /
  230. HORNER4(Float, x, -205.50469464210596, -270.0440832897079, -109.554245632316, -11.275355431307334, 1)
  231. ); }
  232. };
  233. template<int branch>
  234. double LambertW(const double x);
  235. template<>
  236. double
  237. LambertW<0>(const double x)
  238. {
  239. typedef double d;
  240. return Y5(
  241. (Branch<d, 0>::BranchPointExpansion<8>(x)),
  242. x < -0.367679,
  243. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, 0>::BranchPointExpansion<10>(x))),
  244. x < -0.311,
  245. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, 0, 1>::Approximation(x))),
  246. x < 1.38,
  247. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, 0, 2>::Approximation(x))),
  248. x < 236,
  249. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, 0>::AsymptoticExpansion<6-1>(x)))
  250. );
  251. }
  252. template<>
  253. double
  254. LambertW<-1>(const double x)
  255. {
  256. typedef double d;
  257. return Y7(
  258. (Branch<d, -1>::BranchPointExpansion<8>(x)),
  259. x < -0.367579,
  260. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, -1>::BranchPointExpansion<4>(x))),
  261. x < -0.366079,
  262. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 7>::Approximation(x))),
  263. x < -0.289379,
  264. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 4>::Approximation(x))),
  265. x < -0.0509,
  266. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 5>::Approximation(x))),
  267. x < -0.000131826,
  268. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 6>::Approximation(x))),
  269. x < -6.30957e-31,
  270. (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, -1>::LogRecursion<3>(x)))
  271. );
  272. }
  273. // instantiations
  274. template double LambertW<0>(const double x);
  275. template double LambertW<-1>(const double x);
  276. }