LambertW.cc
Go to the documentation of this file.
1 #include <iostream>
2 #include <cmath>
3 #include <limits>
4 #include <utl/LambertW.h>
5 
6 using namespace std;
7 
8 
9 namespace utl {
10 
11 
12  namespace LambertWDetail {
13 
14 
15  const double kInvE = 1/M_E;
16 
17 
18  template<int n>
19  inline double BranchPointPolynomial(const double p);
20 
21 
22  template<>
23  inline
24  double
26  {
27  return
28  -1 + p;
29  }
30 
31 
32  template<>
33  inline
34  double
36  {
37  return
38  -1 + p*(1 + p*(-1./3));
39  }
40 
41 
42  template<>
43  inline
44  double
46  {
47  return
48  -1 + p*(1 + p*(-1./3 + p*(11./72)));
49  }
50 
51 
52  template<>
53  inline
54  double
56  {
57  return
58  -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540))));
59  }
60 
61 
62  template<>
63  inline
64  double
66  {
67  return
68  -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280)))));
69  }
70 
71 
72  template<>
73  inline
74  double
76  {
77  return
78  -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280
79  + p*(-221./8505))))));
80  }
81 
82 
83  template<>
84  inline
85  double
87  {
88  return
89  -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280
90  + p*(-221./8505 + p*(680863./43545600)))))));
91  }
92 
93 
94  template<>
95  inline
96  double
98  {
99  return
100  -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280
101  + p*(-221./8505 + p*(680863./43545600 + p*(-1963./204120))))))));
102  }
103 
104 
105  template<>
106  inline
107  double
109  {
110  return
111  -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280
112  + p*(-221./8505 + p*(680863./43545600 + p*(-1963./204120
113  + p*(226287557./37623398400.)))))))));
114  }
115 
116 
117  template<int order>
118  inline double AsymptoticExpansion(const double a, const double b);
119 
120 
121  template<>
122  inline
123  double
124  AsymptoticExpansion<0>(const double a, const double b)
125  {
126  return a - b;
127  }
128 
129 
130  template<>
131  inline
132  double
133  AsymptoticExpansion<1>(const double a, const double b)
134  {
135  return a - b + b / a;
136  }
137 
138 
139  template<>
140  inline
141  double
142  AsymptoticExpansion<2>(const double a, const double b)
143  {
144  const double ia = 1 / a;
145  return a - b + b / a * (1 + ia * 0.5*(-2 + b));
146  }
147 
148 
149  template<>
150  inline
151  double
152  AsymptoticExpansion<3>(const double a, const double b)
153  {
154  const double ia = 1 / a;
155  return a - b + b / a *
156  (1 + ia *
157  (0.5*(-2 + b) + ia *
158  1/6.*(6 + b*(-9 + b*2))
159  )
160  );
161  }
162 
163 
164  template<>
165  inline
166  double
167  AsymptoticExpansion<4>(const double a, const double b)
168  {
169  const double ia = 1 / a;
170  return a - b + b / a *
171  (1 + ia *
172  (0.5*(-2 + b) + ia *
173  (1/6.*(6 + b*(-9 + b*2)) + ia *
174  1/12.*(-12 + b*(36 + b*(-22 + b*3)))
175  )
176  )
177  );
178  }
179 
180 
181  template<>
182  inline
183  double
184  AsymptoticExpansion<5>(const double a, const double b)
185  {
186  const double ia = 1 / a;
187  return a - b + b / a *
188  (1 + ia *
189  (0.5*(-2 + b) + ia *
190  (1/6.*(6 + b*(-9 + b*2)) + ia *
191  (1/12.*(-12 + b*(36 + b*(-22 + b*3))) + ia *
192  1/60.*(60 + b*(-300 + b*(350 + b*(-125 + b*12))))
193  )
194  )
195  )
196  );
197  }
198 
199 
200  template<int branch>
201  class Branch {
202 
203  public:
204  template<int order>
205  static double BranchPointExpansion(const double x)
206  { return BranchPointPolynomial<order>(eSign * sqrt(2*(M_E*x+1))); }
207 
208  // Asymptotic expansion
209  // Corless et al. 1996, de Bruijn (1981)
210  template<int order>
211  static
212  double
213  AsymptoticExpansion(const double x)
214  {
215  const double logsx = log(eSign * x);
216  const double logslogsx = log(eSign * logsx);
217  return LambertWDetail::AsymptoticExpansion<order>(logsx, logslogsx);
218  }
219 
220  template<int n>
221  static inline double RationalApproximation(const double x);
222 
223  // Logarithmic recursion
224  template<int order>
225  static inline double LogRecursion(const double x)
226  { return LogRecursionStep<order>(log(eSign * x)); }
227 
228  // generic approximation valid to at least 5 decimal places
229  static inline double Approximation(const double x);
230 
231  private:
232  // enum { eSign = 2*branch + 1 }; this doesn't work on gcc 3.3.3
233  static const int eSign = 2*branch + 1;
234 
235  template<int n>
236  static inline double LogRecursionStep(const double logsx)
237  { return logsx - log(eSign * LogRecursionStep<n-1>(logsx)); }
238  };
239 
240 
241  // Rational approximations
242 
243  template<>
244  template<>
245  inline
246  double
248  {
249  return x*(60 + x*(114 + 17*x)) / (60 + x*(174 + 101*x));
250  }
251 
252 
253  template<>
254  template<>
255  inline
256  double
258  {
259  // branch 0, valid for [-0.31,0.3]
260  return
261  x * (1 + x *
262  (5.931375839364438 + x *
263  (11.392205505329132 + x *
264  (7.338883399111118 + x*0.6534490169919599)
265  )
266  )
267  ) /
268  (1 + x *
269  (6.931373689597704 + x *
270  (16.82349461388016 + x *
271  (16.43072324143226 + x*5.115235195211697)
272  )
273  )
274  );
275  }
276 
277 
278  template<>
279  template<>
280  inline
281  double
283  {
284  // branch 0, valid for [-0.31,0.5]
285  return
286  x * (1 + x *
287  (4.790423028527326 + x *
288  (6.695945075293267 + x * 2.4243096805908033)
289  )
290  ) /
291  (1 + x *
292  (5.790432723810737 + x *
293  (10.986445930034288 + x *
294  (7.391303898769326 + x * 1.1414723648617864)
295  )
296  )
297  );
298  }
299 
300 
301  template<>
302  template<>
303  inline
304  double
306  {
307  // branch 0, valid for [0.3,7]
308  return
309  x * (1 + x *
310  (2.4450530707265568 + x *
311  (1.3436642259582265 + x *
312  (0.14844005539759195 + x * 0.0008047501729129999)
313  )
314  )
315  ) /
316  (1 + x *
317  (3.4447089864860025 + x *
318  (3.2924898573719523 + x *
319  (0.9164600188031222 + x * 0.05306864044833221)
320  )
321  )
322  );
323  }
324 
325 
326  template<>
327  template<>
328  inline
329  double
330  Branch<-1>::RationalApproximation<4>(const double x)
331  {
332  // branch -1, valid for [-0.3,-0.05]
333  return
334  (-7.814176723907436 + x *
335  (253.88810188892484 + x * 657.9493176902304)
336  ) /
337  (1 + x *
338  (-60.43958713690808 + x *
339  (99.98567083107612 + x *
340  (682.6073999909428 + x *
341  (962.1784396969866 + x * 1477.9341280760887)
342  )
343  )
344  )
345  );
346  }
347 
348 
349  // stopping conditions
350 
351  template<>
352  template<>
353  inline
354  double
355  Branch<0>::LogRecursionStep<0>(const double logsx)
356  {
357  return logsx;
358  }
359 
360 
361  template<>
362  template<>
363  inline
364  double
365  Branch<-1>::LogRecursionStep<0>(const double logsx)
366  {
367  return logsx;
368  }
369 
370 
371  template<>
372  inline
373  double
374  Branch<0>::Approximation(const double x)
375  {
376  /*if (x < -kInvE)
377  return numeric_limits<double>::quiet_NaN();
378  else if (x < -0.32358170806015724)
379  return BranchPointExpansion<9>(x);
380  else if (x < 0.14546954290661823)
381  return RationalApproximation<1>(x);
382  else if (x < 8.706658967856612)
383  return RationalApproximation<3>(x);
384  else
385  return AsymptoticExpansion<5>(x);*/
386 
387  if (x < -0.32358170806015724) {
388  if (x < -kInvE)
389  return numeric_limits<double>::quiet_NaN();
390  else if (x < -kInvE+1e-5)
391  return BranchPointExpansion<5>(x);
392  else
393  return BranchPointExpansion<9>(x);
394  } else {
395  if (x < 0.14546954290661823)
396  return RationalApproximation<1>(x);
397  else if (x < 8.706658967856612)
398  return RationalApproximation<3>(x);
399  else
400  return AsymptoticExpansion<5>(x);
401  }
402  }
403 
404 
405  template<>
406  inline
407  double
409  {
410  /*if (x < -kInvE)
411  return numeric_limits<double>::quiet_NaN();
412  else if (x < -kInvE+1e-5)
413  return BranchPointExpansion<5>(x);
414  else if (x < -0.30298541769)
415  return BranchPointExpansion<9>(x);
416  else if (x < -0.051012917658221676)
417  return RationalApproximation<4>(x);
418  else if (x < 0)
419  return LogRecursion<9>(x);
420  else if (x == 0)
421  return -numeric_limits<double>::infinity();
422  else
423  return numeric_limits<double>::quiet_NaN();*/
424 
425  if (x < -0.051012917658221676) {
426  if (x < -kInvE+1e-5) {
427  if (x < -kInvE)
428  return numeric_limits<double>::quiet_NaN();
429  else
430  return BranchPointExpansion<5>(x);
431  } else {
432  if (x < -0.30298541769)
433  return BranchPointExpansion<9>(x);
434  else
435  return RationalApproximation<4>(x);
436  }
437  } else {
438  if (x < 0)
439  return LogRecursion<9>(x);
440  else if (x == 0)
441  return -numeric_limits<double>::infinity();
442  else
443  return numeric_limits<double>::quiet_NaN();
444  }
445  }
446 
447 
448  // iterations
449 
450  inline
451  double
452  HalleyStep(const double x, const double w)
453  {
454  const double ew = exp(w);
455  const double wew = w * ew;
456  const double wewx = wew - x;
457  const double w1 = w + 1;
458  return w - wewx / (ew * w1 - (w + 2) * wewx/(2*w1));
459  }
460 
461 
462  inline
463  double
464  FritschStep(const double x, const double w)
465  {
466  const double z = log(x/w) - w;
467  const double w1 = w + 1;
468  const double q = 2 * w1 * (w1 + (2/3.)*z);
469  const double eps = z / w1 * (q - z) / (q - 2*z);
470  return w * (1 + eps);
471  }
472 
473 
474  template<
475  double IterationStep(const double x, const double w)
476  >
477  inline
478  double
479  Iterate(const double x, double w, const double eps = 1e-6)
480  {
481  for (int i = 0; i < 100; ++i) {
482  const double ww = IterationStep(x, w);
483  if (fabs(ww - w) <= eps)
484  return ww;
485  w = ww;
486  }
487  cerr << "convergence not reached." << endl;
488  return w;
489  }
490 
491 
492  template<
493  double IterationStep(const double x, const double w)
494  >
495  struct Iterator {
496 
497  static
498  double
499  Do(const int n, const double x, double w)
500  {
501  for (int i = 0; i < n; ++i)
502  w = IterationStep(x, w);
503  return w;
504  }
505 
506  template<int n>
507  static
508  double
509  Do(const double x, double w)
510  {
511  for (int i = 0; i < n; ++i)
512  w = IterationStep(x, w);
513  return w;
514  }
515 
516  template<int n, class = void>
517  struct Depth {
518  static double Recurse(const double x, double w)
519  { return Depth<n-1>::Recurse(x, IterationStep(x, w)); }
520  };
521 
522  // stop condition
523  template<class T>
524  struct Depth<1, T> {
525  static double Recurse(const double x, const double w)
526  { return IterationStep(x, w); }
527  };
528 
529  // identity
530  template<class T>
531  struct Depth<0, T> {
532  static double Recurse([[maybe_unused]] const double x, const double w)
533  { return w; }
534  };
535 
536  };
537 
538 
539  } // LambertWDetail
540 
541 
542  template<int branch>
543  double
544  LambertWApproximation(const double x)
545  {
547  }
548 
549  // instantiations
550  template double LambertWApproximation<0>(const double x);
551  template double LambertWApproximation<-1>(const double x);
552 
553 
554  template<int branch>
555  double LambertW(const double x);
556 
557  template<>
558  double
559  LambertW<0>(const double x)
560  {
561  if (fabs(x) > 1e-6 && x > -LambertWDetail::kInvE + 1e-5)
562  return
563  LambertWDetail::
564  Iterator<LambertWDetail::FritschStep>::
565  Depth<1>::
566  Recurse(x, LambertWApproximation<0>(x));
567  else
568  return LambertWApproximation<0>(x);
569  }
570 
571  template<>
572  double
573  LambertW<-1>(const double x)
574  {
575  if (x > -LambertWDetail::kInvE + 1e-5)
576  return
581  else
582  return LambertWApproximation<-1>(x);
583  }
584 
585 } // utl
double AsymptoticExpansion< 5 >(const double a, const double b)
Definition: LambertW.cc:184
double AsymptoticExpansion< 4 >(const double a, const double b)
Definition: LambertW.cc:167
const double kInvE
Definition: LambertW.cc:15
template double LambertWApproximation<-1 >(const double x)
static double AsymptoticExpansion(const double x)
Definition: LambertW.cc:213
double LambertW< 0 >(const double x)
Definition: LambertW.cc:559
double BranchPointPolynomial(const double p)
double AsymptoticExpansion(const double a, const double b)
double AsymptoticExpansion< 3 >(const double a, const double b)
Definition: LambertW.cc:152
template double LambertWApproximation< 0 >(const double x)
double BranchPointPolynomial< 5 >(const double p)
Definition: LambertW.cc:65
double Iterate(const double x, double w, const double eps=1e-6)
Definition: LambertW.cc:479
double AsymptoticExpansion< 0 >(const double a, const double b)
Definition: LambertW.cc:124
static double Recurse(const double x, double w)
Definition: LambertW.cc:518
double BranchPointPolynomial< 9 >(const double p)
Definition: LambertW.cc:108
double BranchPointPolynomial< 4 >(const double p)
Definition: LambertW.cc:55
static double Recurse(const double x, const double w)
Definition: LambertW.cc:525
double LambertW(const double x)
double BranchPointPolynomial< 3 >(const double p)
Definition: LambertW.cc:45
double LambertWApproximation(const double x)
Definition: LambertW.cc:544
double FritschStep(const double x, const double w)
Definition: LambertW.cc:464
static double BranchPointExpansion(const double x)
Definition: LambertW.cc:205
double eps
static double LogRecursion(const double x)
Definition: LambertW.cc:225
double AsymptoticExpansion< 2 >(const double a, const double b)
Definition: LambertW.cc:142
static double Do(const int n, const double x, double w)
Definition: LambertW.cc:499
double AsymptoticExpansion< 1 >(const double a, const double b)
Definition: LambertW.cc:133
double HalleyStep(const double x, const double w)
Definition: LambertW.cc:452
double BranchPointPolynomial< 6 >(const double p)
Definition: LambertW.cc:75
double BranchPointPolynomial< 2 >(const double p)
Definition: LambertW.cc:35
double BranchPointPolynomial< 8 >(const double p)
Definition: LambertW.cc:97
double BranchPointPolynomial< 7 >(const double p)
Definition: LambertW.cc:86
double BranchPointPolynomial< 1 >(const double p)
Definition: LambertW.cc:25
static double Recurse([[maybe_unused]] const double x, const double w)
Definition: LambertW.cc:532
static double Do(const double x, double w)
Definition: LambertW.cc:509
static double LogRecursionStep(const double logsx)
Definition: LambertW.cc:236

, generated on Tue Sep 26 2023.