gsl_specfunc__lambert.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /* specfunc/lambert.c
  2. *
  3. * Copyright (C) 2007 Brian Gough
  4. * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 Gerard Jungman
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or (at
  9. * your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. /* Author: G. Jungman */
  21. #include "gsl__config.h"
  22. #include <math.h>
  23. #include "gsl_math.h"
  24. #include "gsl_errno.h"
  25. #include "gsl_sf_lambert.h"
  26. /* Started with code donated by K. Briggs; added
  27. * error estimates, GSL foo, and minor tweaks.
  28. * Some Lambert-ology from
  29. * [Corless, Gonnet, Hare, and Jeffrey, "On Lambert's W Function".]
  30. */
  31. /* Halley iteration (eqn. 5.12, Corless et al) */
  32. static int
  33. halley_iteration(
  34. double x,
  35. double w_initial,
  36. unsigned int max_iters,
  37. gsl_sf_result * result
  38. )
  39. {
  40. double w = w_initial;
  41. unsigned int i;
  42. for(i=0; i<max_iters; i++) {
  43. double tol;
  44. const double e = exp(w);
  45. const double p = w + 1.0;
  46. double t = w*e - x;
  47. /* printf("FOO: %20.16g %20.16g\n", w, t); */
  48. if (w > 0) {
  49. t = (t/p)/e; /* Newton iteration */
  50. } else {
  51. t /= e*p - 0.5*(p + 1.0)*t/p; /* Halley iteration */
  52. };
  53. w -= t;
  54. tol = 10 * GSL_DBL_EPSILON * GSL_MAX_DBL(fabs(w), 1.0/(fabs(p)*e));
  55. if(fabs(t) < tol)
  56. {
  57. result->val = w;
  58. result->err = 2.0*tol;
  59. return GSL_SUCCESS;
  60. }
  61. }
  62. /* should never get here */
  63. result->val = w;
  64. result->err = fabs(w);
  65. return GSL_EMAXITER;
  66. }
  67. /* series which appears for q near zero;
  68. * only the argument is different for the different branches
  69. */
  70. static double
  71. series_eval(double r)
  72. {
  73. static const double c[12] = {
  74. -1.0,
  75. 2.331643981597124203363536062168,
  76. -1.812187885639363490240191647568,
  77. 1.936631114492359755363277457668,
  78. -2.353551201881614516821543561516,
  79. 3.066858901050631912893148922704,
  80. -4.175335600258177138854984177460,
  81. 5.858023729874774148815053846119,
  82. -8.401032217523977370984161688514,
  83. 12.250753501314460424,
  84. -18.100697012472442755,
  85. 27.029044799010561650
  86. };
  87. const double t_8 = c[8] + r*(c[9] + r*(c[10] + r*c[11]));
  88. const double t_5 = c[5] + r*(c[6] + r*(c[7] + r*t_8));
  89. const double t_1 = c[1] + r*(c[2] + r*(c[3] + r*(c[4] + r*t_5)));
  90. return c[0] + r*t_1;
  91. }
  92. /*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/
  93. int
  94. gsl_sf_lambert_W0_e(double x, gsl_sf_result * result)
  95. {
  96. const double one_over_E = 1.0/M_E;
  97. const double q = x + one_over_E;
  98. if(x == 0.0) {
  99. result->val = 0.0;
  100. result->err = 0.0;
  101. return GSL_SUCCESS;
  102. }
  103. else if(q < 0.0) {
  104. /* Strictly speaking this is an error. But because of the
  105. * arithmetic operation connecting x and q, I am a little
  106. * lenient in case of some epsilon overshoot. The following
  107. * answer is quite accurate in that case. Anyway, we have
  108. * to return GSL_EDOM.
  109. */
  110. result->val = -1.0;
  111. result->err = sqrt(-q);
  112. return GSL_EDOM;
  113. }
  114. else if(q == 0.0) {
  115. result->val = -1.0;
  116. result->err = GSL_DBL_EPSILON; /* cannot error is zero, maybe q == 0 by "accident" */
  117. return GSL_SUCCESS;
  118. }
  119. else if(q < 1.0e-03) {
  120. /* series near -1/E in sqrt(q) */
  121. const double r = sqrt(q);
  122. result->val = series_eval(r);
  123. result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val);
  124. return GSL_SUCCESS;
  125. }
  126. else {
  127. static const unsigned int MAX_ITERS = 10;
  128. double w;
  129. if (x < 1.0) {
  130. /* obtain initial approximation from series near x=0;
  131. * no need for extra care, since the Halley iteration
  132. * converges nicely on this branch
  133. */
  134. const double p = sqrt(2.0 * M_E * q);
  135. w = -1.0 + p*(1.0 + p*(-1.0/3.0 + p*11.0/72.0));
  136. }
  137. else {
  138. /* obtain initial approximation from rough asymptotic */
  139. w = log(x);
  140. if(x > 3.0) w -= log(w);
  141. }
  142. return halley_iteration(x, w, MAX_ITERS, result);
  143. }
  144. }
  145. int
  146. gsl_sf_lambert_Wm1_e(double x, gsl_sf_result * result)
  147. {
  148. if(x > 0.0) {
  149. return gsl_sf_lambert_W0_e(x, result);
  150. }
  151. else if(x == 0.0) {
  152. result->val = 0.0;
  153. result->err = 0.0;
  154. return GSL_SUCCESS;
  155. }
  156. else {
  157. static const unsigned int MAX_ITERS = 32;
  158. const double one_over_E = 1.0/M_E;
  159. const double q = x + one_over_E;
  160. double w;
  161. if (q < 0.0) {
  162. /* As in the W0 branch above, return some reasonable answer anyway. */
  163. result->val = -1.0;
  164. result->err = sqrt(-q);
  165. return GSL_EDOM;
  166. }
  167. if(x < -1.0e-6) {
  168. /* Obtain initial approximation from series about q = 0,
  169. * as long as we're not very close to x = 0.
  170. * Use full series and try to bail out if q is too small,
  171. * since the Halley iteration has bad convergence properties
  172. * in finite arithmetic for q very small, because the
  173. * increment alternates and p is near zero.
  174. */
  175. const double r = -sqrt(q);
  176. w = series_eval(r);
  177. if(q < 3.0e-3) {
  178. /* this approximation is good enough */
  179. result->val = w;
  180. result->err = 5.0 * GSL_DBL_EPSILON * fabs(w);
  181. return GSL_SUCCESS;
  182. }
  183. }
  184. else {
  185. /* Obtain initial approximation from asymptotic near zero. */
  186. const double L_1 = log(-x);
  187. const double L_2 = log(-L_1);
  188. w = L_1 - L_2 + L_2/L_1;
  189. }
  190. return halley_iteration(x, w, MAX_ITERS, result);
  191. }
  192. }
  193. /*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/
  194. #include "gsl_specfunc__eval.h"
  195. double gsl_sf_lambert_W0(double x)
  196. {
  197. EVAL_RESULT(gsl_sf_lambert_W0_e(x, &result));
  198. }
  199. double gsl_sf_lambert_Wm1(double x)
  200. {
  201. EVAL_RESULT(gsl_sf_lambert_Wm1_e(x, &result));
  202. }