/* 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; #if 0 # define NAMESPACE_UTL_BEGIN namespace utl { # define NAMESPACE_UTL_END } #else # define NAMESPACE_UTL_BEGIN # define NAMESPACE_UTL_END #endif NAMESPACE_UTL_BEGIN namespace LambertWDetail { template inline double BranchPointPolynomial(const double p); /*template<> inline double BranchPointPolynomial<0>(const double / *p* /) { return -1; // 0 } template<> inline double BranchPointPolynomial<1>(const double p) { return -1 + // 0 p*( 1 // 1 ); } template<> inline double BranchPointPolynomial<2>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 // 2 )); } template<> inline double BranchPointPolynomial<3>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 // 3 ))); } template<> inline double BranchPointPolynomial<4>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 // 4 )))); } template<> inline double BranchPointPolynomial<5>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 // 5 ))))); } template<> inline double BranchPointPolynomial<6>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 // 6 )))))); }*/ template<> inline double BranchPointPolynomial<7>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 // 7 ))))))); } /*template<> inline double BranchPointPolynomial<8>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 // 8 )))))))); } template<> inline double BranchPointPolynomial<9>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 // 9 ))))))))); } template<> inline double BranchPointPolynomial<10>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 // 10 )))))))))); } template<> inline double BranchPointPolynomial<11>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 // 11 ))))))))))); } template<> inline double BranchPointPolynomial<12>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 // 12 )))))))))))); } template<> inline double BranchPointPolynomial<13>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 // 13 ))))))))))))); } template<> inline double BranchPointPolynomial<14>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 + // 13 p*(-0.67206163115613620e-3 // 14 )))))))))))))); } template<> inline double BranchPointPolynomial<15>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 + // 13 p*(-0.67206163115613620e-3 + // 14 p*( 0.44247306181462090e-3 // 15 ))))))))))))))); } template<> inline double BranchPointPolynomial<16>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 + // 13 p*(-0.67206163115613620e-3 + // 14 p*( 0.44247306181462090e-3 + // 15 p*(-0.29267722472962746e-3 // 16 )))))))))))))))); } template<> inline double BranchPointPolynomial<17>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 + // 13 p*(-0.67206163115613620e-3 + // 14 p*( 0.44247306181462090e-3 + // 15 p*(-0.29267722472962746e-3 + // 16 p*( 0.19438727605453930e-3 // 17 ))))))))))))))))); } template<> inline double BranchPointPolynomial<18>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 + // 13 p*(-0.67206163115613620e-3 + // 14 p*( 0.44247306181462090e-3 + // 15 p*(-0.29267722472962746e-3 + // 16 p*( 0.19438727605453930e-3 + // 17 p*(-0.12957426685274883e-3 // 18 )))))))))))))))))); } template<> inline double BranchPointPolynomial<19>(const double p) { return -1 + // 0 p*( 1 + // 1 p*(-0.333333333333333333e0 + // 2 p*( 0.152777777777777777e0 + // 3 p*(-0.79629629629629630e-1 + // 4 p*( 0.44502314814814814e-1 + // 5 p*(-0.25984714873603760e-1 + // 6 p*( 0.15635632532333920e-1 + // 7 p*(-0.96168920242994320e-2 + // 8 p*( 0.60145432529561180e-2 + // 9 p*(-0.38112980348919993e-2 + // 10 p*( 0.24408779911439826e-2 + // 11 p*(-0.15769303446867841e-2 + // 12 p*( 0.10262633205076071e-2 + // 13 p*(-0.67206163115613620e-3 + // 14 p*( 0.44247306181462090e-3 + // 15 p*(-0.29267722472962746e-3 + // 16 p*( 0.19438727605453930e-3 + // 17 p*(-0.12957426685274883e-3 + // 18 p*( 0.86650358052081260e-4 // 19 ))))))))))))))))))); }*/ /* -1 1 -0.333333333333333333e0 0.152777777777777777e0 -0.79629629629629630e-1 0.44502314814814814e-1 -0.25984714873603760e-1 0.15635632532333920e-1 -0.96168920242994320e-2 0.60145432529561180e-2 -0.38112980348919993e-2 0.24408779911439826e-2 -0.15769303446867841e-2 0.10262633205076071e-2 -0.67206163115613620e-3 0.44247306181462090e-3 -0.29267722472962746e-3 0.19438727605453930e-3 -0.12957426685274883e-3 0.86650358052081260e-4 */ 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, const 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); NAMESPACE_UTL_END