Jpp 19.3.0
the software that should make you happy
Loading...
Searching...
No Matches
LambertW.hh
Go to the documentation of this file.
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#ifndef _utl_LambertW_h_
16#define _utl_LambertW_h_
17
18#include <cmath>
19#include <limits>
20
21namespace utl {
22
23 template<typename Float, class Tag, unsigned int order>
24 struct Polynomial {
25 static inline Float Coeff();
26 static inline Float Coeff(const Float x);
27 };
28
29
30 template<typename Float, class Tag, unsigned int order>
31 struct Horner {
32 static Float Recurse(const Float term, const Float x)
34 static Float RecurseAlt(const Float term, const Float x)
36 static Float Eval(const Float x)
40 //
41 static Float Recurse(const Float term, const Float x, const Float y)
43 static Float Eval(const Float x, const Float y)
45 };
46
47
48 template<typename Float, class Tag>
49 struct Horner<Float, Tag, 0> {
50 static Float Recurse(const Float term, const Float x)
51 { return term*x + Polynomial<Float, Tag, 0>::Coeff(); }
52 static Float Eval(const Float x)
54 //
55 static Float Recurse(const Float term, const Float x, const Float y)
56 { return term*x + Polynomial<Float, Tag, 0>::Coeff(y); }
57 static Float Eval(const Float x, const Float y)
59 };
60
61
62#define HORNER_COEFF(_Tag_, _i_, _c_) \
63 template<typename Float> struct Polynomial<Float, _Tag_, _i_> { static Float Coeff() { return Float(_c_); } }
64#define HORNER_COEFF2(_Tag_, _i_, _c_y_) \
65 template<typename Float> struct Polynomial<Float, _Tag_, _i_> { static Float Coeff(const Float y) { return Float(_c_y_); } }
66
67
68#define HORNER0(F, x, c0) (F)(c0)
69#define HORNER1(F, x, c1, c0) HORNER0(F, x, (F)(c1)*(x) + (F)(c0) )
70#define HORNER2(F, x, c2, c1, c0) HORNER1(F, x, (F)(c2)*(x) + (F)(c1), c0)
71#define HORNER3(F, x, c3, c2, c1, c0) HORNER2(F, x, (F)(c3)*(x) + (F)(c2), c1, c0)
72#define HORNER4(F, x, c4, c3, c2, c1, c0) HORNER3(F, x, (F)(c4)*(x) + (F)(c3), c2, c1, c0)
73#define HORNER5(F, x, c5, c4, c3, c2, c1, c0) HORNER4(F, x, (F)(c5)*(x) + (F)(c4), c3, c2, c1, c0)
74#define HORNER6(F, x, c6, c5, c4, c3, c2, c1, c0) HORNER5(F, x, (F)(c6)*(x) + (F)(c5), c4, c3, c2, c1, c0)
75#define HORNER7(F, x, c7, c6, c5, c4, c3, c2, c1, c0) HORNER6(F, x, (F)(c7)*(x) + (F)(c6), c5, c4, c3, c2, c1, c0)
76#define HORNER8(F, x, c8, c7, c6, c5, c4, c3, c2, c1, c0) HORNER7(F, x, (F)(c8)*(x) + (F)(c7), c6, c5, c4, c3, c2, c1, c0)
77#define HORNER9(F, x, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0) HORNER8(F, x, (F)(c9)*(x) + (F)(c8), c7, c6, c5, c4, c3, c2, c1, c0)
78
79}
80
81// fork macro to keep the tree balanced
82#define Y2(d1, c12, d2) \
83 ((c12) ? (d1) : (d2))
84#define Y3(d1, c12, d2, c23, d3) \
85 Y2(Y2(d1, c12, d2), c23, d3)
86#define Y4(d1, c12, d2, c23, d3, c34, d4) \
87 Y3(d1, c12, d2, c23, Y2(d3, c34, d4))
88#define Y5(d1, c12, d2, c23, d3, c34, d4, c45, d5) \
89 Y4(Y2(d1, c12, d2), c23, d3, c34, d4, c45, d5)
90#define Y6(d1, c12, d2, c23, d3, c34, d4, c45, d5, c56, d6) \
91 Y5(d1, c12, d2, c23, d3, c34, d4, c45, Y2(d5, c56, d6))
92#define Y7(d1, c12, d2, c23, d3, c34, d4, c45, d5, c56, d6, c67, d7) \
93 Y6(d1, c12, d2, c23, Y2(d3, c34, d4), c45, d5, c56, d6, c67, d7)
94
95
96namespace utl {
97
98 class BranchPoint { };
101 HORNER_COEFF(BranchPoint, 2, -0.333333333333333333e0);
102 HORNER_COEFF(BranchPoint, 3, 0.152777777777777777e0);
103 HORNER_COEFF(BranchPoint, 4, -0.79629629629629630e-1);
104 HORNER_COEFF(BranchPoint, 5, 0.44502314814814814e-1);
105 HORNER_COEFF(BranchPoint, 6, -0.25984714873603760e-1);
106 HORNER_COEFF(BranchPoint, 7, 0.15635632532333920e-1);
107 HORNER_COEFF(BranchPoint, 8, -0.96168920242994320e-2);
108 HORNER_COEFF(BranchPoint, 9, 0.60145432529561180e-2);
109 HORNER_COEFF(BranchPoint, 10, -0.38112980348919993e-2);
110 HORNER_COEFF(BranchPoint, 11, 0.24408779911439826e-2);
111 HORNER_COEFF(BranchPoint, 12, -0.15769303446867841e-2);
112 HORNER_COEFF(BranchPoint, 13, 0.10262633205076071e-2);
113 HORNER_COEFF(BranchPoint, 14, -0.67206163115613620e-3);
114 HORNER_COEFF(BranchPoint, 15, 0.44247306181462090e-3);
115 HORNER_COEFF(BranchPoint, 16, -0.29267722472962746e-3);
116 HORNER_COEFF(BranchPoint, 17, 0.19438727605453930e-3);
117 HORNER_COEFF(BranchPoint, 18, -0.12957426685274883e-3);
118 HORNER_COEFF(BranchPoint, 19, 0.86650358052081260e-4);
119
120 template<unsigned int order>
122 //HORNER_COEFF(AsymptoticPolynomialB<0>, 0, 0);
123 //HORNER_COEFF(AsymptoticPolynomialB<0>, 1, -1);
124 //HORNER_COEFF(AsymptoticPolynomialB<1>, 0, 0);
125 //HORNER_COEFF(AsymptoticPolynomialB<1>, 1, 1);
145 //HORNER_COEFF2(AsymptoticPolynomialA, 0, (Horner<Float, AsymptoticPolynomialB<0>, 1>::Eval(y)));
147 //HORNER_COEFF2(AsymptoticPolynomialA, 1, (Horner<Float, AsymptoticPolynomialB<1>, 1>::Eval(y)));
153
154
155 template<typename Float, int order>
156 inline
157 Float
158 AsymptoticExpansionImpl(const Float a, const Float b)
159 {
161 }
162
163
164 template<typename Float, int branch, int order>
166 enum { eSign = 2*branch + 1 };
167 static Float Step(const Float logsx)
168 { return logsx - log(eSign * LogRecursionImpl<Float, branch, order-1>::Step(logsx)); }
169 };
170
171 template<typename Float, int branch>
172 struct LogRecursionImpl<Float, branch, 0> {
173 static Float Step(const Float logsx) { return logsx; }
174 };
175
176
177 template<typename Float, int branch>
178 class Branch {
179 public:
180 template<int order>
181 static Float BranchPointExpansion(const Float x)
182 { return Horner<Float, BranchPoint, order>::Eval(eSign * sqrt(2*(Float(M_E)*x+1))); }
183
184 // Asymptotic expansion: Corless et al. 1996, de Bruijn (1981)
185 template<int order>
186 static
187 Float
188 AsymptoticExpansion(const Float x)
189 {
190 const Float logsx = log(eSign * x);
191 const Float logslogsx = log(eSign * logsx);
192 return AsymptoticExpansionImpl<Float, order>(logsx, logslogsx);
193 }
194
195 // Logarithmic recursion
196 template<int order>
197 static Float LogRecursion(const Float x)
199
200 private:
201 enum { eSign = 2*branch + 1 };
202 };
203
204
205 // iterations
206
207 template<typename Float>
208 inline
209 Float
210 HalleyStep(const Float x, const Float w)
211 {
212 const Float ew = exp(w);
213 const Float wew = w * ew;
214 const Float wewx = wew - x;
215 const Float w1 = w + 1;
216 return w - wewx / (ew * w1 - (w + 2) * wewx/(2*w1));
217 }
218
219
220 template<typename Float>
221 inline
222 Float
223 FritschStep(const Float x, const Float w)
224 {
225 const Float z = log(x/w) - w;
226 const Float w1 = w + 1;
227 const Float q = 2 * w1 * (w1 + Float(2./3)*z);
228 const Float eps = z / w1 * (q - z) / (q - 2*z);
229 return w * (1 + eps);
230 }
231
232
233 template<typename Float>
234 inline
235 Float
236 SchroederStep(const Float x, const Float w)
237 {
238 const Float y = x * exp(-w);
239 const Float f0 = w - y;
240 const Float f1 = 1 + y;
241 const Float f00 = f0*f0;
242 const Float f11 = f1*f1;
243 const Float f0y = f0*y;
244 return w - 4*f0*(6*f1*(f11 + f0y) + f00*y) / (f11*(24*f11 + 36*f0y) + f00*(6*y*y + 8*f1*y + f0y));
245 }
246
247
248 template<
249 typename Float,
250 double IterationStep(const Float x, const Float w)
251 >
252 struct Iterator {
253
254 template<int n, class = void>
255 struct Depth {
256 static Float Recurse(const Float x, Float w)
257 { return Depth<n-1>::Recurse(x, IterationStep(x, w)); }
258 };
259
260 // stop condition
261 template<class T>
262 struct Depth<1, T> {
263 static Float Recurse(const Float x, const Float w) { return IterationStep(x, w); }
264 };
265
266 // identity
267 template<class T>
268 struct Depth<0, T> {
269 static Float Recurse(const Float x, const Float w) { return w; }
270 };
271
272 };
273
274
275 // Rational approximations
276
277 template<typename Float, int branch, int n>
278 struct Pade {
279 static inline Float Approximation(const Float x);
280 };
281
282
283 template<typename Float>
284 struct Pade<Float, 0, 1> {
285 static inline Float Approximation(const Float x)
286 { return x * HORNER4(Float, x, 0.07066247420543414, 2.4326814530577687, 6.39672835731526, 4.663365025836821, 0.99999908757381) /
287 HORNER4(Float, x, 1.2906660139511692, 7.164571775410987, 10.559985088953114, 5.66336307375819, 1); }
288 };
289
290
291 template<typename Float>
292 struct Pade<Float, 0, 2> {
293 static inline Float Approximation(const Float x)
294 { const Float y = log(Float(0.5)*x) - 2;
295 return 2 + y * HORNER3(Float, y, 0.00006979269679670452, 0.017110368846615806, 0.19338607770900237, 0.6666648896499793) /
296 HORNER2(Float, y, 0.0188060684652668, 0.23451269827133317, 1); }
297 };
298
299
300 template<typename Float>
301 struct Pade<Float, -1, 4> {
302 static inline Float Approximation(const Float x)
303 { return HORNER4(Float, x, -2793.4565508841197, -1987.3632221106518, 385.7992853617571, 277.2362778379572, -7.840776922133643) /
304 HORNER4(Float, x, 280.6156995997829, 941.9414019982657, 190.64429338894644, -63.93540494358966, 1); }
305 };
306
307
308 template<typename Float>
309 struct Pade<Float, -1, 5> {
310 static inline Float Approximation(const Float x)
311 { const Float y = log(-x);
312 return -exp(
313 HORNER3(Float, y, 0.16415668298255184, -3.334873920301941, 2.4831415860003747, 4.173424474574879) /
314 HORNER3(Float, y, 0.031239411487374164, -1.2961659693400076, 4.517178492772906, 1)
315 ); }
316 };
317
318
319 template<typename Float>
320 struct Pade<Float, -1, 6> {
321 static inline Float Approximation(const Float x)
322 { const Float y = log(-x);
323 return -exp(
324 HORNER4(Float, y, 0.000026987243254533254, -0.007692106448267341, 0.28793461719300206, -1.5267058884647018, -0.5370669268991288) /
325 HORNER4(Float, y, 3.6006502104930343e-6, -0.0015552463555591487, 0.08801194682489769, -0.8973922357575583, 1)
326 ); }
327 };
328
329
330 template<typename Float>
331 struct Pade<Float, -1, 7> {
332 static inline Float Approximation(const Float x)
333 { return -1 - sqrt(
334 HORNER4(Float, x, 988.0070769375508, 1619.8111957356814, 989.2017745708083, 266.9332506485452, 26.875022558546036) /
335 HORNER4(Float, x, -205.50469464210596, -270.0440832897079, -109.554245632316, -11.275355431307334, 1)
336 ); }
337 };
338
339
340 template<int branch>
341 double LambertW(const double x);
342
343
344 template<>
345 double
346 LambertW<0>(const double x)
347 {
348 typedef double d;
349 return Y5(
351 x < -0.367679,
352 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, 0>::BranchPointExpansion<10>(x))),
353 x < -0.311,
354 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, 0, 1>::Approximation(x))),
355 x < 1.38,
356 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, 0, 2>::Approximation(x))),
357 x < 236,
358 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, 0>::AsymptoticExpansion<6-1>(x)))
359 );
360 }
361
362
363 template<>
364 double
365 LambertW<-1>(const double x)
366 {
367 typedef double d;
368 return Y7(
370 x < -0.367579,
371 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, -1>::BranchPointExpansion<4>(x))),
372 x < -0.366079,
373 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 7>::Approximation(x))),
374 x < -0.289379,
375 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 4>::Approximation(x))),
376 x < -0.0509,
377 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 5>::Approximation(x))),
378 x < -0.000131826,
379 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Pade<d, -1, 6>::Approximation(x))),
380 x < -6.30957e-31,
381 (Iterator<d, HalleyStep<d> >::Depth<1>::Recurse(x, Branch<d, -1>::LogRecursion<3>(x)))
382 );
383 }
384
385
386 // instantiations
387 template double LambertW<0>(const double x);
388 template double LambertW<-1>(const double x);
389
390
391 template<int branch>
392 double LambertW(const double x);
393
394
395 inline
396 double
397 LambertW(const int branch, const double x)
398 {
399 switch (branch) {
400 case -1: return LambertW<-1>(x);
401 case 0: return LambertW<0>(x);
402 default: return std::numeric_limits<double>::quiet_NaN();
403 }
404 }
405}
406
407#endif
#define HORNER3(F, x, c3, c2, c1, c0)
Definition Horner.h:70
#define HORNER2(F, x, c2, c1, c0)
Definition Horner.h:69
#define HORNER_COEFF(_Tag_, _i_, _c_)
Definition Horner.h:61
#define HORNER_COEFF2(_Tag_, _i_, _c_y_)
Definition Horner.h:63
#define HORNER4(F, x, c4, c3, c2, c1, c0)
Definition Horner.h:71
#define Y5(d1, c12, d2, c23, d3, c34, d4, c45, d5)
Definition LambertW.hh:88
#define Y7(d1, c12, d2, c23, d3, c34, d4, c45, d5, c56, d6, c67, d7)
Definition LambertW.hh:92
static Float AsymptoticExpansion(const Float x)
Definition LambertW.hh:188
static Float LogRecursion(const Float x)
Definition LambertW.hh:197
static Float BranchPointExpansion(const Float x)
Definition LambertW.hh:181
Definition Horner.h:20
double LambertW(const double x)
Float FritschStep(const Float x, const Float w)
Definition LambertW.hh:223
Float HalleyStep(const Float x, const Float w)
Definition LambertW.hh:210
double LambertW< 0 >(const double x)
Definition LambertW.hh:346
Float AsymptoticExpansionImpl(const Float a, const Float b)
Definition LambertW.hh:158
Float SchroederStep(const Float x, const Float w)
Definition LambertW.hh:236
static Float Eval(const Float x)
Definition LambertW.hh:52
static Float Eval(const Float x, const Float y)
Definition LambertW.hh:57
static Float Recurse(const Float term, const Float x)
Definition LambertW.hh:50
static Float Recurse(const Float term, const Float x, const Float y)
Definition LambertW.hh:55
static Float Recurse(const Float term, const Float x)
Definition LambertW.hh:32
static Float EvalAlt(const Float x)
Definition LambertW.hh:38
static Float RecurseAlt(const Float term, const Float x)
Definition LambertW.hh:34
static Float Eval(const Float x, const Float y)
Definition LambertW.hh:43
static Float Eval(const Float x)
Definition LambertW.hh:36
static Float Recurse(const Float term, const Float x, const Float y)
Definition LambertW.hh:41
static Float Recurse(const Float x, const Float w)
Definition LambertW.hh:269
static Float Recurse(const Float x, const Float w)
Definition LambertW.hh:263
static Float Recurse(const Float x, Float w)
Definition LambertW.hh:256
static Float Step(const Float logsx)
Definition LambertW.hh:173
static Float Step(const Float logsx)
Definition LambertW.hh:167
static Float Approximation(const Float x)
Definition LambertW.hh:302
static Float Approximation(const Float x)
Definition LambertW.hh:310
static Float Approximation(const Float x)
Definition LambertW.hh:321
static Float Approximation(const Float x)
Definition LambertW.hh:332
static Float Approximation(const Float x)
Definition LambertW.hh:285
static Float Approximation(const Float x)
Definition LambertW.hh:293
static Float Approximation(const Float x)
static Float Coeff(const Float x)
static Float Coeff()