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.

326 lines
7.2KB

  1. /*
  2. Implementation of the Fukushima method for the Lambert W function
  3. Copyright (C) 2015 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. /*
  16. This code is based on the following publication and its author's fortran code:
  17. Toshio Fukushima, "Precise and fast computation of Lambert W-functions without
  18. transcendental function evaluations", J. Comp. Appl. Math. 244 (2013) 77-89.
  19. */
  20. #include <FukushimaLambertW.h>
  21. #include <cmath>
  22. #include <limits>
  23. #include <iostream>
  24. using namespace std;
  25. namespace Fukushima {
  26. double
  27. LambertWSeries(const double p)
  28. {
  29. static const double q[] = {
  30. -1,
  31. +1,
  32. -0.333333333333333333,
  33. +0.152777777777777778,
  34. -0.0796296296296296296,
  35. +0.0445023148148148148,
  36. -0.0259847148736037625,
  37. +0.0156356325323339212,
  38. -0.00961689202429943171,
  39. +0.00601454325295611786,
  40. -0.00381129803489199923,
  41. +0.00244087799114398267,
  42. -0.00157693034468678425,
  43. +0.00102626332050760715,
  44. -0.000672061631156136204,
  45. +0.000442473061814620910,
  46. -0.000292677224729627445,
  47. +0.000194387276054539318,
  48. -0.000129574266852748819,
  49. +0.0000866503580520812717,
  50. -0.0000581136075044138168
  51. };
  52. const double ap = abs(p);
  53. if (ap < 0.01159)
  54. return
  55. -1 +
  56. p*(1 +
  57. p*(q[2] +
  58. p*(q[3] +
  59. p*(q[4] +
  60. p*(q[5] +
  61. p*q[6]
  62. )))));
  63. else if (ap < 0.0766)
  64. return
  65. -1 +
  66. p*(1 +
  67. p*(q[2] +
  68. p*(q[3] +
  69. p*(q[4] +
  70. p*(q[5] +
  71. p*(q[6] +
  72. p*(q[7] +
  73. p*(q[8] +
  74. p*(q[9] +
  75. p*q[10]
  76. )))))))));
  77. else
  78. return
  79. -1 +
  80. p*(1 +
  81. p*(q[2] +
  82. p*(q[3] +
  83. p*(q[4] +
  84. p*(q[5] +
  85. p*(q[6] +
  86. p*(q[7] +
  87. p*(q[8] +
  88. p*(q[9] +
  89. p*(q[10] +
  90. p*(q[11] +
  91. p*(q[12] +
  92. p*(q[13] +
  93. p*(q[14] +
  94. p*(q[15] +
  95. p*(q[16] +
  96. p*(q[17] +
  97. p*(q[18] +
  98. p*(q[19] +
  99. p*q[20]
  100. )))))))))))))))))));
  101. }
  102. inline
  103. double
  104. LambertW0ZeroSeries(const double z)
  105. {
  106. return
  107. z*(1 -
  108. z*(1 -
  109. z*(1.5 -
  110. z*(2.6666666666666666667 -
  111. z*(5.2083333333333333333 -
  112. z*(10.8 -
  113. z*(23.343055555555555556 -
  114. z*(52.012698412698412698 -
  115. z*(118.62522321428571429 -
  116. z*(275.57319223985890653 -
  117. z*(649.78717234347442681 -
  118. z*(1551.1605194805194805 -
  119. z*(3741.4497029592385495 -
  120. z*(9104.5002411580189358 -
  121. z*(22324.308512706601434 -
  122. z*(55103.621972903835338 -
  123. z*136808.86090394293563
  124. ))))))))))))))));
  125. }
  126. inline
  127. double
  128. FinalResult(const double w, const double y)
  129. {
  130. const double f0 = w - y;
  131. const double f1 = 1 + y;
  132. const double f00 = f0 * f0;
  133. const double f11 = f1 * f1;
  134. const double f0y = f0 * y;
  135. return w - 4 * f0 * (6 * f1 * (f11 + f0y) + f00 * y) /
  136. (f11 * (24 * f11 + 36 * f0y) +
  137. f00 * (6 * y * y + 8 * f1 * y + f0y));
  138. }
  139. double
  140. LambertW0(const double z)
  141. {
  142. static double e[66];
  143. static double g[65];
  144. static double a[12];
  145. static double b[12];
  146. if (!e[0]) {
  147. const double e1 = 1 / M_E;
  148. double ej = 1;
  149. e[0] = M_E;
  150. e[1] = 1;
  151. g[0] = 0;
  152. for (int j = 1, jj = 2; jj < 66; ++jj) {
  153. ej *= M_E;
  154. e[jj] = e[j] * e1;
  155. g[j] = j * ej;
  156. j = jj;
  157. }
  158. a[0] = sqrt(e1);
  159. b[0] = 0.5;
  160. for (int j = 0, jj = 1; jj < 12; ++jj) {
  161. a[jj] = sqrt(a[j]);
  162. b[jj] = b[j] * 0.5;
  163. j = jj;
  164. }
  165. }
  166. if (abs(z) < 0.05)
  167. return LambertW0ZeroSeries(z);
  168. if (z < -0.35) {
  169. const double p2 = 2 * (M_E * z + 1);
  170. if (p2 > 0)
  171. return LambertWSeries(sqrt(p2));
  172. if (p2 == 0)
  173. return -1;
  174. cerr << "(lambertw0) Argument out of range. z=" << z << endl;
  175. return numeric_limits<double>::quiet_NaN();
  176. }
  177. int n;
  178. for (n = 0; n <= 2; ++n)
  179. if (g[n] > z)
  180. goto line1;
  181. n = 2;
  182. for (int j = 1; j <= 5; ++j) {
  183. n *= 2;
  184. if (g[n] > z)
  185. goto line2;
  186. }
  187. cerr << "(lambertw0) Argument too large. z=" << z << endl;
  188. return numeric_limits<double>::quiet_NaN();
  189. line2:
  190. {
  191. int nh = n / 2;
  192. for (int j = 1; j <= 5; ++j) {
  193. nh /= 2;
  194. if (nh <= 0)
  195. break;
  196. if (g[n-nh] > z)
  197. n -= nh;
  198. }
  199. }
  200. line1:
  201. --n;
  202. int jmax = 8;
  203. if (z <= -0.36)
  204. jmax = 12;
  205. else if (z <= -0.3)
  206. jmax = 11;
  207. else if (n <= 0)
  208. jmax = 10;
  209. else if (n <= 1)
  210. jmax = 9;
  211. double y = z * e[n+1];
  212. double w = n;
  213. for (int j = 0; j < jmax; ++j) {
  214. const double wj = w + b[j];
  215. const double yj = y * a[j];
  216. if (wj < yj) {
  217. w = wj;
  218. y = yj;
  219. }
  220. }
  221. return FinalResult(w, y);
  222. }
  223. double
  224. LambertWm1(const double z)
  225. {
  226. static double e[64];
  227. static double g[64];
  228. static double a[12];
  229. static double b[12];
  230. if (!e[0]) {
  231. const double e1 = 1 / M_E;
  232. double ej = e1;
  233. e[0] = M_E;
  234. g[0] = -e1;
  235. for (int j = 0, jj = 1; jj < 64; ++jj) {
  236. ej *= e1;
  237. e[jj] = e[j] * M_E;
  238. g[jj] = -(jj+1) * ej;
  239. j = jj;
  240. }
  241. a[0] = sqrt(M_E);
  242. b[0] = 0.5;
  243. for (int j = 0, jj = 1; jj < 12; ++jj) {
  244. a[jj] = sqrt(a[j]);
  245. b[jj] = b[j] * 0.5;
  246. j = jj;
  247. }
  248. }
  249. if (z >= 0) {
  250. cerr << "(lambertwm1) Argument out of range. z=" << z << endl;
  251. return numeric_limits<double>::quiet_NaN();
  252. }
  253. if (z < -0.35) {
  254. const double p2 = 2 * (M_E * z + 1);
  255. if (p2 > 0)
  256. return LambertWSeries(-sqrt(p2));
  257. if (p2 == 0)
  258. return -1;
  259. cerr << "(lambertwm1) Argument out of range. z=" << z << endl;
  260. return numeric_limits<double>::quiet_NaN();
  261. }
  262. int n = 2;
  263. if (g[n - 1] > z)
  264. goto line1;
  265. for (int j = 1; j <= 5; ++j) {
  266. n *= 2;
  267. if (g[n - 1] > z)
  268. goto line2;
  269. }
  270. cerr << "(lambertwm1) Argument too small. z=" << z << endl;
  271. return numeric_limits<double>::quiet_NaN();
  272. line2:
  273. {
  274. int nh = n / 2;
  275. for (int j = 1; j <= 5; ++j) {
  276. nh /= 2;
  277. if (nh <= 0)
  278. break;
  279. if (g[n-nh - 1] > z)
  280. n -= nh;
  281. }
  282. }
  283. line1:
  284. --n;
  285. int jmax = 11;
  286. if (n >= 8)
  287. jmax = 8;
  288. else if (n >= 3)
  289. jmax = 9;
  290. else if (n >= 2)
  291. jmax = 10;
  292. double w = -n;
  293. double y = z * e[n - 1];
  294. for (int j = 0; j < jmax; ++j) {
  295. const double wj = w - b[j];
  296. const double yj = y * a[j];
  297. if (wj < yj) {
  298. w = wj;
  299. y = yj;
  300. }
  301. }
  302. return FinalResult(w, y);
  303. }
  304. }