Having Fun with Lambert W(x) Function arXiv

0 downloads 0 Views 787KB Size Report
Mar 8, 2010 - The number of accurate decimal places ∆(x) is shown for two ranges, linear ... so that it gives us a number of correct decimal places the ...
arXiv:1003.1628v1 [cs.MS] 8 Mar 2010

Having Fun with Lambert W( x) Function Darko Veberiˇc a,b,c a

b

University of Nova Gorica, Slovenia IK, Forschungszentrum Karlsruhe, Germany c J. Stefan Institute, Ljubljana, Slovenia

June 2009

Abstract This short note presents the Lambert W( x ) function and its possible application in the framework of physics related to the Pierre Auger Observatory. The actual numerical implementation in C++ consists of Halley’s and Fritsch’s iteration with branch-point expansion, asymptotic series and rational fits as initial approximations.

0 W0

WHxL

-1 -2

W-1

-3 -4

-0.4

-0.2

0.0

0.2

0.4

0.6

0.8

1.0

x

Figure 1: The two branches of the Lambert W function, W−1 ( x ) in blue and W0 ( x ) in red. The branching point at (−e−1 , −1) is denoted with a green dash.

1

Introduction

The Lambert W( x ) function is defined as the inverse function of y exp y = x,

(1)

y = W( x ),

(2)

W( x ) exp W( x ) = x.

(3)

the solution being given by or shortly Since the x 7→ x exp x mapping is not injective, no unique inverse of the x exp x function exists. As can be seen in Fig. 1, the Lambert function has two real branches with a branching point located at (−e−1 , −1). The bottom branch, W−1 ( x ), is defined in the interval x ∈ [−e−1 , 0] and has a negative singularity for x → 0− . The upper branch is defined for x ∈ [−e−1 , ∞]. The earliest mention of problem of Eq. (1) is attributed to Euler. However, Euler himself credited Lambert for his previous work in this subject. The W( x ) function started to be named after Lambert only recently, in the last 10 years or so. The letter W was chosen by the first implementation of the W( x ) function in the Maple computer software. Recently, the W( x ) function amassed quite a following in the mathematical community. Its most faithful proponents are suggesting to elevate it among the present set of elementary functions, such as sin( x ), cos( x ), ln( x ), etc. The main argument for doing so is the fact that it is the root of the simplest exponential polynomial function. While the Lambert W function is simply called W in the mathematics software tool Maple, in the Mathematica computer algebra framework this function is implemented under the name ProductLog (in the recent versions an alias LambertW is also supported). There are numerous, well documented applications of W( x ) in mathematics, physics, and computer science [1, 3]. Here we will give two examples that arise from the physics related to the Pierre Auger Observatory.

0.6

1.0

0.5

0.8

gHx; xmax L

MHxL

0.4 0.3

0.6

0.4

0.2 0.2

0.1 0.0

0.0 -2

0

2

4

6

8

0

5

10

x

15 x

20

25

30

Figure 2: Left: The Moyal function M( x ). Right: A family of one-parametric GaisserHillas functions g( x; xmax ) for xmax in the range from 1 to 10 with step 1.

1.1

Moyal function

Moyal function is defined as  M( x ) = exp − 21 ( x + exp(− x )) .

(4)

Its inverse can be written in terms of the two branches of the Lambert W function, 1 2 M− ± ( x ) = W0,−1 (− x ) − 2 ln x.

(5)

and can be seen in Fig. 2 (left). Within the event reconstruction of the data taken by the Pierre Auger Observatory, the Moyal function is used for phenomenological recovery of the saturated signals from the photomultipliers.

1.2

Gaisser-Hillas function

In astrophysics the Gaisser-Hillas function is used to model the longitudinal particle density in a cosmic-ray air showers [4]. We can show that the inverse of the three-parametric Gaisser-Hillas function, X − X0 G ( X; X0 , Xmax , λ) = Xmax − X0 

 Xmaxλ−X0

 exp

Xmax − X λ

 ,

(6)

is intimately related to the Lambert W function. Using rescale substitutions, X − X0 and λ Xmax − X0 = , λ

x= xmax

(7) (8)

the Gaisser-Hillas function is modified into a function of one parameter only,  g( x; xmax ) =

x xmax

 xmax exp( xmax − x ).

(9)

The family of one-parametric Gaisser-Hillas functions is shown in Fig. 2 (right). The problem of finding an inverse, g( x; xmax ) ≡ a (10) for 0 < a 6 1, can be rewritten into



x xmax

 exp −

x



xmax

= − a1/xmax e−1 .

(11)

According to the definition (1), the two (real) solutions for x are obtained from the two branches of the Lambert W function, √ (12) x1,2 = − xmax W0,−1 (− a1/xmax e−1 ) = − xmax W0,−1 (− xmax a/e). Note that the branch −1 or 0 simply chooses the right or left side relative to the maximum, respectively.

2

Numerics

Before moving to the actual implementation let us review some of the possible nimerical and analytical approaches.

2.1

Recursion

For x > 0 and W( x ) > 0 we can take the natural logarithm of (3) and rearrange it, W( x ) = ln x − ln W( x ).

(13)

It is clear, that a possible analytical expression for W( x ) exhibits a degree of self similarity. The W( x ) function has multiple branches in the complex domain. Due to the x > 0 and W( x ) > 0 conditions, the Eq. (13) represents the positive part of the W0 ( x ) principal branch, but as it turns out, in this form it is suitable for evaluation when W0 ( x ) > 1, i.e. when x > e. Unrolling the self-similarity (13) as a recursive relation, one obtains the following curious expression for W0 ( x ), W0 ( x ) = ln x − ln(ln x − ln(ln x − . . . )),

(14)

or in a shorthand of a continued logarithm, W0 ( x ) = ln

x ln lnx x

.

(15)

···

The above expression is clearly a form of successive approximation, the final result given by the limit, when it exists. For x < 0 and W( x ) < 0 we can multiply both sides of Eq. (3) with −1, take logarithm, and rewrite it to get a similar expansion for the W−1 ( x ) branch, W( x ) = ln(− x ) − ln(− W( x )).

(16)

Again, this leads to a similar recursive expression, W−1 ( x ) = ln(− x ) − ln(−(ln(− x ) − ln(−(ln(− x ) − . . . )))),

(17)

or as a continued logarithm, W−1 ( x ) = ln

−x −x . − ln − ln −x

(18)

···

[n]

For this continued logarithm we will use the symbol R−1 ( x ) where n denotes the depth of the recursion. Starting from yet another rearrangement of Eq. (3), W( x ) =

x , exp W( x )

(19)

we can obtain a recursion relation for the −e−1 < x < e part of the principal branch W0 ( x ) < 1, x W0 ( x ) = . (20) exp expx x ...

2.2

Halley’s iteration

We can apply Halley’s root-finding method [8] to derive an iteration scheme for f (y) = W (y) − x by writing the second-order Taylor series f (y) = f (yn ) + f 0 (yn ) (y − yn ) + 12 f 00 (yn ) (y − yn )2 + · · ·

(21)

Since root y of f (y) satisfies f (y) = 0 we can approximate the left-hand side of Eq. (21) with 0 and replace y with yn+1 . Rewriting the obtained result into y n +1 = y n −

f 0 (y

n) +

f (yn ) 1 00 2 f ( y n ) ( y n +1

− yn )

(22)

and using Newton’s method yn+1 − yn = − f (yn )/ f 00 (yn ) on the last bracket, we arrive at the expression for the Halley’s iteration for Lambert function Wn+1 = Wn +

tn , tn sn − un

(23)

where tn = Wn exp Wn − x, Wn + 2 sn = , 2(Wn + 1) un = (Wn + 1) exp Wn .

(24) (25) (26)

This method is of the third order, i.e. having Wn = W( x ) + O(ε) will give Wn+1 = W( x ) + O(ε3 ). Supplying this iteration with sufficiently accurate first approximation of the order of O(10−4 ) will thus give a machine-size floating point precission O(10−16 ) in at least two iterations.

2.3

Fritsch’s iteration

For both branches of Lambert function a more efficient iteration scheme exists [9], Wn+1 = Wn (1 + ε n ),

(27)

where ε n is the relative difference of successive approximations at iteration n, εn =

Wn+1 − Wn . Wn

The relative difference can be expressed as    qn − zn zn , εn = 1 + Wn qn − 2zn

(28)

(29)

where x − Wn , Wn  qn = 2(1 + Wn ) 1 + Wn + 23 zn . zn = ln

(30) (31)

The error term in this iteration is of a fourth order, i.e. with Wn = W( x ) + O(ε n ) we get Wn+1 = W( x ) + O(ε4n ). Supplying this iteration with a sufficiently reasonable first guess, accurate to the order of O(10−4 ), will therefore deliver machine-size floating point precission O(10−16 ) in only one iteration and excessive O(10−64 ) in two! We have to find reliable first order approximation that can be fed into the Fritsch iteration. Due to the lively landscape of the Lambert function properties, the approximations will have to be found in all the particular ranges of the function behavior.

3

Initial approximations

The following section deals with finding the appropriate initial approximations in the whole definition ranges of the two branches of the Lambert function.

3.1

Branch-point expansion

The inverse of the Lambert function, W−1 (y) = y exp y, has two extrema located at W−1 (−1) = −e−1 and W−1 (−∞) = 0− . Expanding W−1 (y) to the second order around the minimum at y = −1 we obtain 1 ( y + 1)2 W−1 ( y ) ≈ − + . (32) e 2e The inverse W−1 (y) is thus in the lowest order approximated with a parabolic term implying that the Lambert function will have square-root behavior in the vicinity of the branch point x = −e−1 , q W−1,0 ( x ) ≈ −1 ∓

2(1 + ex ).

(33)

To obtain the additional terms in expression (33) we proceed by defining an inverse function, centered and rescaled around the minimum, i.e. f (y) = 2(e W−1 (y − 1) + 1). Due

W0

-1

W0

-2

0.0

WHxL

WHxL

-3 -4

-0.5

-5 -1.0

W-1

-6

W-1 -7

-0.3

-0.2

0.0

-0.1

-0.3

-0.2

-0.1 x

x

0.0

0.1

0.2

Figure 3: Successive orders of the branch-point expansion for the W−1 ( x ) on the left and W0 ( x ) on the right. to the centering and rescaling the Taylor series of this function around y = 0 becomes particularly neat, 1 5 f (y) = y2 + 32 y3 + 41 y4 + 15 y +··· (34) Using this Taylor expansion we can derive coefficients [10] of the corresponding inverse function   z−2 −1 f (z) = 1 + W = (35) 2e

= z1/2 − 31 z +

11 3/2 72 z



43 2 540 z

+···

From Eq. (35) we see that z = 2(1 + ex ). Using p± ( x ) = ± variable we can write this series expansion as

p

(36)

2(1 + ex ) as independent

n

[n]

W−1,0 ( x ) ≈ B−1,0 ( x ) =

∑ bi pi∓ (x),

(37)

i =0

where the lowest few coefficients bi are

3.2

i

0

1

2

3

4

5

6

7

8

9

bi

-1

1

− 13

11 72

43 − 540

769 17 280

− 8221 505

680 863 43 545 600

1963 − 204 120

226 287 557 37 623 398 400

Asymptotic series

Another useful tool is the asymptotic expansion [2] where using A( a, b) = a − b + ∑ ∑ Ckm a−k−m−1 bm+1 k

(38)

m

where Ckm are related to the Stirling number of the first kind, the Lambert function can be expressed as W−1,0 ( x ) = A(ln(∓ x ), ln(∓ ln(∓ x ))) (39)

with a = ln x, b = ln ln x for the W0 branch and a = ln(− x ), b = ln(− ln(− x )) for the W−1 branch. The first few terms are b b(−2 + b) b(6 − 9b + 2b2 ) A( a, b) = a − b + + + + (40) a 2a2 6a3 b(−12 + 36b − 22b2 + 3b3 ) b(60 − 300b + 350b2 − 125b3 + 12b4 ) +··· + + 60a5 12a4

3.3

Rational fits

A useful quick-and-dirty approach to the functional approximation is to generate large enough sample of data points {wi exp wi , wi }. These points evidently lie on the Lambert function. Within some appropriately chosen range of wi values the points are fitted with a rational approximation ∑ ai x i Q( x ) = i , (41) ∑ i bi x i varying the order of the polynomials in the nominator and denominator, and choosing the one that has the lowest maximal absolute residual in a particular interval of interest. For the W0 ( x ) branch, the first set of equally-spaced wi component was chosen in a range that produced wi exp wi values in an interval [−0.3, 0]. The optimal rational fit turned out to be 1 + a1 x + a2 x 2 + a3 x 3 + a4 x 4 Q0 ( x ) = x (42) 1 + b1 x + b2 x2 + b3 x3 + b4 x4 [1]

where the coefficients for this first approximation Q0 ( x ) are i ai bi

1

2

3

4

5.931375839364438 6.931373689597704

11.39220550532913 16.82349461388016

7.33888339911111 16.43072324143226

0.653449016991959 5.115235195211697

For the second fit of the W0 ( x ) branch a wi range was chosen so that the wi exp wi values [2] were produced in the interval [0.3, 2e] giving rise to the second optimal rational fit Q0 ( x ) of the same form as in Eq. (42) but with coefficients i ai bi

1

2

3

4

2.445053070726557 3.444708986486002

1.343664225958226 3.292489857371952

0.148440055397592 0.916460018803122

0.0008047501729130 0.0530686404483322

For the W−1 ( x ) branch one rational approximation of the form Q −1 ( x ) =

a0 + a1 x + a2 x 2 1 + b1 x + b2 x2 + b3 x3 + b4 x4 + b5 x5

(43)

with the coefficients i ai bi i bi

0

1

2

3

-7.81417672390744

253.88810188892484 -60.43958713690808

657.9493176902304 99.9856708310761

682.6073999909428

4

5

962.1784396969866

1477.9341280760887

20

15

15

10

10

D

D

20

5

5

0

0 -0.3

-0.2

0.0

-0.1

0.1

0.2

0.3

1

10

x

100

1000

104

105

x

Figure 4: Combining different approximations of W0 ( x ) into final piecewise function. The number of accurate decimal places ∆( x ) is shown for two ranges, linear interval [−e−1 , 0.3] on the left and logarithmic interval [0.3, 105 ] on the right. The [9] approximation are branch-point expansion B0 ( x ) from Eq. (37) in blue, rational fits [1]

[2]

Q0 ( x ) and Q0 ( x ) from Eq. (42) in black and red, respectively, and asymptotic series A0 ( x ) from Eq. (40) in green. is enough.

4

Implementation

e ( x ) of the Lambert function W( x ) To quantify the accuracy of a particular approximation W we can introduce a quantity ∆( x ) defined as e ( x ) − W( x )|, ∆( x ) = − log10 |W

(44)

e ( x ) is producing so that it gives us a number of correct decimal places the approximation W for some parameter x. In Fig. 4 all mentioned approximations for the W0 ( x ) are shown in the linear interval [−e−1 , 0.3] on the left and logarithmic interval [0.3, 105 ] on the right. For each of the approximations an use interval is chosen so that the number of accurate decimal places is maximized over the whole definition range. For the W0 ( x ) branch the resulting piecewise approximation  [9]  B0 ( x )     Q [1] ( x ) 0 e 0 (x) = W [2]  Q  0 (x)    A0 ( x )

; −e−1 6 x < −0.32358170806015724 ; −0.32358170806015724 6 x < 0.14546954290661823 ; 0.14546954290661823 6 x < 8.706658967856612 ; 8.706658967856612 6 x < ∞

(45)

is accurate in the definition range [−e−1 , 7] to at least 5 decimal places and to at least 3 [9] [1] decimal places in the whole definition range. The B0 ( x ) is from Eq. (37), Q0 ( x ) and [2]

Q0 ( x ) are from Eq. (42), and A0 ( x ) is from Eq. (40).

20

15

15

10

10

D

D

20

5

5

0

0 -0.3

-0.2

0.0

-0.1

0.1

0.2

0.3

x

1

10

100

1000

104

105

x

20

20

15

15

10

10

D

D

e 0 ( x ) (black) from Fig. 4 after Figure 5: Final values of the combined approximation W one step of the Halley iteration (red) and one step of the Fritsch iteration (blue).

5 0

5

-0.35 -0.30 -0.25 -0.20 -0.15 -0.10 -0.05 0.00 x

0

-0.35 -0.30 -0.25 -0.20 -0.15 -0.10 -0.05 0.00 x

Figure 6: Left: Approximations of the W−1 ( x ) branch. The branch point expansion [9] B−1 ( x ) is shown in blue, the rational approximation Q−1 ( x ) in black, and the loga[9]

rithmic recursion R−1 in red. Right: Combined approximation is accurate to at least 5 decimal places in the whole definition range. The results after applying one step of the Halley iteration are shown in red and after one step of the Fritsch iteration in blue. e 0 ( x ) is shown in Fig. 5 in black line. Using this The final piecewise approximation W approximation a single step of the Halley iteration (in red) and the Fritsch iteration (in blue) is performed and the resulting number of accurate decimal places is shown. As can be seen both iterations produce machine-size accurate floating point numbers in the whole definition interval except for the [9, 110] interval where the Halley method requires another step of the iteration. For that reason we have decided to use only (one step of) the Fritsch iteration in the C++ implementation of the Lambert function. In Fig. 6 (left) the same procedure is shown for the W−1 ( x ) branch. The final approximation  [9] −1   B−1 ( x ) ; −e 6 x < −0.30298541769 e −1 ( x ) = Q−1 ( x ) ; −0.30298541769 6 x < −0.051012917658221676 W (46)   [9] R−1 ( x ) ; −0.051012917658221676 6 x < 0

is accurate to at least 5 decimal places in the whole definition range [−e−1 , 0] and where [9] [9] B−1 ( x ) is from Eq. (37), Q−1 ( x ) is from Eq. (43), and R−1 ( x ) is from Eq. (18). e −1 ( x ) is shown in black line. The values In Fig. 6 (right) the combined approximation W after one step of the Halley iteration are shown in red and after one step of the Fritsch iteration in blue. Similarly as for the previous branch, the Fritsch iteration is superior, yielding machine-size accurate results in the whole definition range, while the Halley is accurate to at least 13 decimal places.

References [1] R.M. Corless, G.H. Gonnet, D.E.G. Hare, D.J. Jeffrey, and D.E. Knuth, On the Lambert W function, Adv. Comput. Math. 5 (1996) 329. [2] N.G. de Bruijn, Asymptotic Methods in Analysis, New York, Dover (1981) 27. [3] R.M. Corless, G.H. Gonnet, D.E.G. Hare, and D.J. Jeffrey, Lambert’s W Function in Maple, Maple Technical Newsletter 9 (1993) 12. [4] T.K. Gaisser and A.M. Hillas, Proceedings of the 15th Internation Cosmic-Ray Conference, Plavdiv, 8 (1977) 353. [5] D.J. Jeffrey, R.M. Corless, D.E.G. Hare, and D.E. Knuth, Sur l’inversion de yα ey au moyen des nombers de Stirling associ´es, Comptes Rendus Acad. Sci. Paris 320 (1995) 1449. [6] N.J.A. Sloane, A handbook of integer sequences, Academic Press (1973); Database of integer sequences (WWW version) http://netlib.att.com/math/sloane/doc/eisbt0.html [7] I.C. Maris¸, M. Roth, and T. Schmidt, A Phenomenological Method to Recover the Signal from Saturated Stations, GAP-2006-012. [8] T.R. Scavo and J.B. Thoo, On the Geometry of Halley’s Method, Amer. Math. Monthly 102 (1995) 417. [9] F.N. Fritsch, R.E. Shafer, and W.P. Crowley, Algorithm 443: solution of the transcendental equation w ew = x, Commun. ACM 16 (1973) 123. [10] P.M. Morse and H. Feshbach, Methods of Theoretical Physics, Part I, McGraw-Hill, New York (1953) 411.

A

Implementation in C++

Sources are available also from http://www.ung.si/~darko/LambertW.tar.gz

A.1

Lambert.h

GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see .

/* Implementation of Lambert W function */ Copyright (C) 2009 Darko Veberic, [email protected]

#include This program is free software: you can redistribute it and/or modify #include it under the terms of the GNU General Public License as published by #include #include "LambertW.h" the Free Software Foundation, either version 3 of the License, or (at your option) any later version. using namespace std; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of namespace LambertWDetail { MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 25 Jun 2009 */ #ifndef _utl_LambertW_h_ #define _utl_LambertW_h_

/** Approximate Lambert W function Accuracy at least 5 decimal places in all definition range. See LambertW() for details. \param branch: valid values are 0 and -1 \param x: real-valued argument \f$\geq-1/e\f$ \ingroup math */ template double LambertWApproximation(const double x);

/** Lambert W function Lambert function \f$y={\rm W}(x)\f$ is defined as a solution to the \f$x=ye^y\f$ expression and is also known as "product logarithm". Since the inverse of \f$ye^y\f$ is not single-valued, the Lambert function has two real branches \f${\rm W}_0\f$ and \f${\rm W}_{-1}\f$. \f${\rm W}_0(x)\f$ has real values in the interval \f$[-1/e,\infty]\f$ and \f${\rm W}_{-1}(x)\f$ has real values in the interval \f$[-1/e,0]\f$. Accuracy is the nominal double type resolution (16 decimal places). \param branch: valid values are 0 and -1 \param x: real-valued argument \f$\geq-1/e\f$ (range depends on branch) */ template double LambertW(const double x);

const double kInvE = 1/M_E;

template inline double BranchPointPolynomial(const double p);

template inline double BranchPointPolynomial(const double p) { return -1 + p; }

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3)); }

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72))); }

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540)))); }

#endif

A.2

Lambert.cc

/* Implementation of Lambert W function

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280))))); }

Copyright (C) 2009 Darko Veberic, [email protected] This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280 + p*(-221./8505))))));

}

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280 + p*(-221./8505 + p*(680863./43545600))))))); }

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280 + p*(-221./8505 + p*(680863./43545600 + p*(-1963./204120)))))))); }

template inline double BranchPointPolynomial(const double p) { return -1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280 + p*(-221./8505 + p*(680863./43545600 + p*(-1963./204120 + p*(226287557./37623398400.))))))))); }

template inline double AsymptoticExpansion(const double a, const double b);

template inline double AsymptoticExpansion(const double a, const double b) { return a - b; }

template inline double AsymptoticExpansion(const double a, const double b) { return a - b + b / a; }

template inline double AsymptoticExpansion(const double a, const double b) { const double ia = 1 / a; return a - b + b / a * (1 + ia * 0.5*(-2 + b)); }

template inline double AsymptoticExpansion(const double a, const double b) { const double ia = 1 / a; return a - b + b / a * (1 + ia * (0.5*(-2 + b) + ia * 1/6.*(6 + b*(-9 + b*2)) ) ); }

template inline double AsymptoticExpansion(const double a, const double b) { const double ia = 1 / a;

return a - b + b / a * (1 + ia * (0.5*(-2 + b) + ia * (1/6.*(6 + b*(-9 + b*2)) + ia * 1/12.*(-12 + b*(36 + b*(-22 + b*3))) ) ) ); }

template inline double AsymptoticExpansion(const double a, const double b) { const double ia = 1 / a; return a - b + b / a * (1 + ia * (0.5*(-2 + b) + ia * (1/6.*(6 + b*(-9 + b*2)) + ia * (1/12.*(-12 + b*(36 + b*(-22 + b*3))) + ia * 1/60.*(60 + b*(-300 + b*(350 + b*(-125 + b*12)))) ) ) ) ); }

template class Branch { public: template static double BranchPointExpansion(const double x) { return BranchPointPolynomial(eSign * sqrt(2*(M_E*x+1))); } // Asymptotic expansion // Corless et al. 1996, de Bruijn (1981) template static double AsymptoticExpansion(const double x) { const double logsx = log(eSign * x); const double logslogsx = log(eSign * logsx); return LambertWDetail::AsymptoticExpansion(logsx, logslogsx); } template static inline double RationalApproximation(const double x); // Logarithmic recursion template static inline double LogRecursion(const double x) { return LogRecursionStep(log(eSign * x)); } // generic approximation valid to at least 5 decimal places static inline double Approximation(const double x); private: // enum { eSign = 2*branch + 1 }; this doesn’t work on gcc 3.3.3 static const int eSign = 2*branch + 1; template static inline double LogRecursionStep(const double logsx) { return logsx - log(eSign * LogRecursionStep(logsx)); } };

// Rational approximations template template inline double Branch::RationalApproximation(const double x) { return x*(60 + x*(114 + 17*x)) / (60 + x*(174 + 101*x)); }

template template inline double Branch::RationalApproximation(const double x) { // branch 0, valid for [-0.31,0.3]

return x * (1 + x * (5.931375839364438 + x * (11.392205505329132 + x * (7.338883399111118 + x*0.6534490169919599) ) ) ) / (1 + x * (6.931373689597704 + x * (16.82349461388016 + x * (16.43072324143226 + x*5.115235195211697) ) ) ); }

template template inline double Branch::RationalApproximation(const double x) { // branch 0, valid for [-0.31,0.5] return x * (1 + x * (4.790423028527326 + x * (6.695945075293267 + x * 2.4243096805908033) ) ) / (1 + x * (5.790432723810737 + x * (10.986445930034288 + x * (7.391303898769326 + x * 1.1414723648617864) ) ) ); }

template template inline double Branch::RationalApproximation(const double x) { // branch 0, valid for [0.3,7] return x * (1 + x * (2.4450530707265568 + x * (1.3436642259582265 + x * (0.14844005539759195 + x * 0.0008047501729129999) ) ) ) / (1 + x * (3.4447089864860025 + x * (3.2924898573719523 + x * (0.9164600188031222 + x * 0.05306864044833221) ) ) ); }

template template inline double Branch::RationalApproximation(const double x) { // branch -1, valid for [-0.3,-0.05] return (-7.814176723907436 + x * (253.88810188892484 + x * 657.9493176902304) ) / (1 + x * (-60.43958713690808 + x * (99.98567083107612 + x * (682.6073999909428 + x * (962.1784396969866 + x * 1477.9341280760887) ) ) ) ); }

// stopping conditions

template template inline double Branch::LogRecursionStep(const double logsx) { return logsx; }

template template inline double Branch::LogRecursionStep(const double logsx) { return logsx; }

template inline double Branch::Approximation(const double x) { if (x < -0.32358170806015724) { if (x < -kInvE) return numeric_limits::quiet_NaN(); else if (x < -kInvE+1e-5) return BranchPointExpansion(x); else return BranchPointExpansion(x); } else { if (x < 0.14546954290661823) return RationalApproximation(x); else if (x < 8.706658967856612) return RationalApproximation(x); else return AsymptoticExpansion(x); } }

template inline double Branch::Approximation(const double x) { if (x < -0.051012917658221676) { if (x < -kInvE+1e-5) { if (x < -kInvE) return numeric_limits::quiet_NaN(); else return BranchPointExpansion(x); } else { if (x < -0.30298541769) return BranchPointExpansion(x); else return RationalApproximation(x); } } else { if (x < 0) return LogRecursion(x); else if (x == 0) return -numeric_limits::infinity(); else return numeric_limits::quiet_NaN(); } }

// iterations inline double HalleyStep(const double x, const double w) { const double ew = exp(w); const double wew = w * ew; const double wewx = wew - x; const double w1 = w + 1; return w - wewx / (ew * w1 - (w + 2) * wewx/(2*w1)); }

inline double FritschStep(const double x, const double w) {

const double z = log(x/w) - w; const double w1 = w + 1; const double q = 2 * w1 * (w1 + (2/3.)*z); const double eps = z / w1 * (q - z) / (q - 2*z); return w * (1 + eps); }

template< double IterationStep(const double x, const double w) > inline double Iterate(const double x, double w, const double eps = 1e-6) { for (int i = 0; i < 100; ++i) { const double ww = IterationStep(x, w); if (fabs(ww - w) -LambertWDetail::kInvE + 1e-5) return LambertWDetail:: Iterator:: Depth:: Recurse(x, LambertWApproximation(x)); else return LambertWApproximation(x); } template double LambertW(const double x) { if (x > -LambertWDetail::kInvE + 1e-5) return LambertWDetail:: Iterator:: Depth:: Recurse(x, LambertWApproximation(x)); else return LambertWApproximation(x); } // instantiations template double LambertW(const double x); template double LambertW(const double x);