/*
Implementation of Lambert W function
Copyright (C) 2009 Darko Veberic, darko.veberic@ung.si
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
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 .
*/
/*
Changes:
30 Nov 2012: bug fix for W0(0), reported by Timothy R. Waters
*/
#include
#include
#include "LambertW.h"
using namespace std;
namespace LambertWDetail {
template
inline double BranchPointPolynomial(const double p);
/*template<>
inline
double
BranchPointPolynomial<1>(const double p)
{
return
-1 + p;
}
template<>
inline
double
BranchPointPolynomial<2>(const double p)
{
return
-1 + p*(1 + p*(-1./3));
}
template<>
inline
double
BranchPointPolynomial<3>(const double p)
{
return
-1 + p*(1 + p*(-1./3 + p*(11./72)));
}
template<>
inline
double
BranchPointPolynomial<4>(const double p)
{
return
-1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540))));
}
template<>
inline
double
BranchPointPolynomial<5>(const double p)
{
return
-1 + p*(1 + p*(-1./3 + p*(11./72 + p*(-43./540 + p*(769./17280)))));
}
template<>
inline
double
BranchPointPolynomial<6>(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<7>(const double p)
{
return
-1 +
p*( 1 +
p*(-3.33333333333333315e-1 +
p*( 1.52777777777777790e-1 +
p*(-7.96296296296296335e-2 +
p*( 4.45023148148148140e-2 +
p*(-2.59847148736037613e-2 +
p*( 1.56356325323339200e-2 )))))));
}
/*template<>
inline
double
BranchPointPolynomial<8>(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<9>(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<0>(const double a, const double b)
{
return a - b;
}
template<>
inline
double
AsymptoticExpansion<1>(const double a, const double b)
{
return a - b + b / a;
}
template<>
inline
double
AsymptoticExpansion<2>(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<3>(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<4>(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<5>(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:
enum { eBranch = branch };
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 };
template
static inline double LogRecursionStep(const double logsx)
{ return logsx - log(eSign * LogRecursionStep(logsx)); }
};
// Rational approximations
/*template<>
template<>
inline
double
Branch<0>::RationalApproximation<0>(const double x)
{
return x*(60 + x*(114 + 17*x)) / (60 + x*(174 + 101*x));
}*/
template<>
template<>
inline
double
Branch<0>::RationalApproximation<1>(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<0>::RationalApproximation<2>(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<0>::RationalApproximation<3>(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<-1>::RationalApproximation<4>(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<0>::LogRecursionStep<0>(const double logsx)
{
return logsx;
}
template<>
template<>
inline
double
Branch<-1>::LogRecursionStep<0>(const double logsx)
{
return logsx;
}
/** Approximate Lambert W function
only \f$W_0(x)\f$ and \f$W_{-1}(x)\f$ have real values
\param branch: valid values are 0 and -1
*/
template<>
inline
double
Branch<0>::Approximation(const double x)
{
return (x < 0.14546954290661823) ?
((x < -0.32358170806015724) ?
BranchPointExpansion<7>(x) : RationalApproximation<1>(x))
:
((x < 8.706658967856612) ?
RationalApproximation<3>(x) : AsymptoticExpansion<5>(x));
}
template<>
inline
double
Branch<-1>::Approximation(const double x)
{
return (x > -0.035) ?
LogRecursion<5>(x) :
((x > -0.311) ?
RationalApproximation<4>(x) :
BranchPointExpansion<7>(x));
}
// 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 + 0.666666666666666666*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) <= eps)
return ww;
w = ww;
}
cerr << "convergence not reached." << endl;
return w;
}
template<
double IterationStep(const double x, const double w)
>
struct Iterator {
/*static
double
Do(const int n, const double x, double w)
{
for (int i = 0; i < n; ++i)
w = IterationStep(x, w);
return w;
}
template
static
double
Do(const double x, double w)
{
for (int i = 0; i < n; ++i)
w = IterationStep(x, w);
return w;
}*/
template
struct Depth {
static double Recurse(const double x, double w)
{ return Depth::Recurse(x, IterationStep(x, w)); }
};
// stop condition
template
struct Depth<1, T> {
static double Recurse(const double x, const double w)
{ return IterationStep(x, w); }
};
// identity
template
struct Depth<0, T> {
static double Recurse(const double x, const double w)
{ return w; }
};
};
} // LambertWDetail
template
double
LambertWApproximation(const double x)
{
return LambertWDetail::Branch::Approximation(x);
}
// instantiations
template double LambertWApproximation<0>(const double x);
template double LambertWApproximation<-1>(const double x);
template
double
LambertW(const double x)
{
if (branch == 0 && x == 0)
return 0;
return
LambertWDetail::
Iterator::
Depth<1>::
Recurse(x, LambertWApproximation(x));
}
// instantiations
template double LambertW<0>(const double x);
template double LambertW<-1>(const double x);