/* * lambertw - Lambert's W-function * * Copyright (C) 2013,2021 Christoph Zurnieden * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * Calc 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 Lesser General * Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL. You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Under source code control: 2013/08/11 01:31:28 * File existed as early as: 2013 */ static resource_debug_level; resource_debug_level = config("resource_debug", 0); /* R. M. Corless and G. H. Gonnet and D. E. G. Hare and D. J. Jeffrey and D. E. Knuth, "On the Lambert W Function", Advances n Computational Mathematics, 329--359, (1996) http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.112.6117 D. J. Jeffrey, D. E. G. Hare, R. M. Corless, "Unwinding the branches of the Lambert W function", The Mathematical Scientist, 21, pp 1-7, (1996) http://www.apmaths.uwo.ca/~djeffrey/Offprints/wbranch.pdf Darko Verebic, "Having Fun with Lambert W(x) Function" arXiv:1003.1628v1, March 2010, http://arxiv.org/abs/1003.1628 Winitzki, S. "Uniform Approximations for Transcendental Functions", In Part 1 of Computational Science and its Applications - ICCSA 2003, Lecture Notes in Computer Science, Vol. 2667, Springer-Verlag, Berlin, 2003, 780-789. DOI 10.1007/3-540-44839-X_82 A copy may be found by Google. */ static true = 1; static false = 0; /* Branch 0, Winitzki (2003) , the well known Taylor series*/ define __CZ__lambertw_0(z,eps){ local a=2.344e0, b=0.8842e0, c=0.9294e0, d=0.5106e0, e=-1.213e0; local y=sqrt(2*exp(1)*z+2); return (2*ln(1+b*y)-ln(1+c*ln(1+d*y))+e)/(1+1/(2*ln(1+b*y)+2*a)); } /* branch -1 */ define __CZ__lambertw_m1(z,eps){ local wn k; /* Cut-off found in Maxima */ if(z < 0.3) return __CZ__lambertw_app(z,eps); wn = z; /* Verebic (2010) eqs. 16-18*/ for(k=0;k<10;k++){ wn = ln(-z)-ln(-wn); } return wn; } /* generic approximation series for 1+W((z-2)/(2 e)) Corless et al (1996) (4.22) Verebic (2010) eqs. 35-37; more coefficients given at the end of sect. 3.1 or online http://www.wolframalpha.com/input/? i=taylor+%28+1%2Bproductlog%28+%28z-2%29%2F%282*e%29+%29+%29 or by using the function lambertw_series_print() after running lambertw_series(z,eps,branch,terms) at least once with the wanted number of terms and z = 1 (which might throw an error because the series will not converge in anybodies lifetime for something that far from the branch point). */ define __CZ__lambertw_app(z,eps){ local b0=-1, b1=1, b2=-1/3, b3=11/72; local y=sqrt(2*exp(1)*z+2); return b0 + ( y * (b1 + (y * (b2 + (b3 * y))))); } static __CZ__Ws_a; static __CZ__Ws_c; static __CZ__Ws_len=0; define lambertw_series_print(){ local k; for(k=0;k<__CZ__Ws_len;k++){ print num(__CZ__Ws_c[k]):"/":den(__CZ__Ws_c[k]):"*p^":k; } } /* The series is fast but only if _very_ close to the branch point The exact branch must be given explicitly, e.g.: ; lambertw(-exp(-1)+.001)-lambertw_series(-exp(-1)+.001,epsilon()*1e-10,0) -0.14758879113205794065490184399030194122136720202792- 0.00000000000000000000000000000000000000000000000000i ; lambertw(-exp(-1)+.001)-lambertw_series(-exp(-1)+.001,epsilon()*1e-10,1) 0.00000000000000000000000000000000000000000000000000- 0.00000000000000000000000000000000000000000000000000i */ define lambertw_series(z,eps,branch,terms){ local k l limit tmp sum A C P PP epslocal; if(!isnull(terms)) limit = terms; else limit = 100; if(isnull(eps)) eps = epsilon(epsilon()*1e-10); epslocal = epsilon(eps); P = sqrt(2*(exp(1)*z+1)); if(branch != 0) P = -P; tmp=0;sum=0;PP=P; __CZ__Ws_a = mat[limit+1]; __CZ__Ws_c = mat[limit+1]; __CZ__Ws_len = limit; /* c0 = -1; c1 = 1 a0 = 2; a1 =-1 */ __CZ__Ws_c[0] = -1; __CZ__Ws_c[1] = 1; __CZ__Ws_a[0] = 2; __CZ__Ws_a[1] = -1; sum += __CZ__Ws_c[0]; sum += __CZ__Ws_c[1] * P; PP *= P; for(k=2;kbranchpoint && z < 0){##print "0"; ret = __CZ__lambertw_m1(z,eps);} if(im(z)>=0 && abs(z-branchpoint) <= bparea){##print "1"; ret = __CZ__lambertw_app(z,eps);} ret =ln(z) + 2*pi()*1i*branch - ln(ln(z)+2*pi()*1i*branch); } else ret = ln(z) + 2*pi()*1i*branch - ln(ln(z)+2*pi()*1i*branch); /* Such a high precision is only needed _very_ close to the branchpoint and might even be insufficient if z has not been computed with sufficient precision itself (M below was calculated by Mathematica and also with the series above with epsilon(1e-200)): ; epsilon(1e-50) 0.00000000000000000001 ; display(50) 20 ; M=-0.9999999999999999999999997668356018402875796636464119050387 ; lambertw(-exp(-1)+1e-50,0)-M -0.00000000000000000000000002678416515423276355643684 ; epsilon(1e-60) 0.0000000000000000000000000000000000000000000000000 ; A=-exp(-1)+1e-50 ; epsilon(1e-50) 0.00000000000000000000000000000000000000000000000000 ; lambertw(A,0)-M -0.00000000000000000000000000000000000231185460220585 ; lambertw_series(A,epsilon(),0)-M -0.00000000000000000000000000000000000132145133161626 ; epsilon(1e-100) 0.00000000000000000000000000000000000000000000000001 ; A=-exp(-1)+1e-50 ; epsilon(1e-65) 0.00000000000000000000000000000000000000000000000000 ; lambertw_series(A,epsilon(),0)-M 0.00000000000000000000000000000000000000000000000000 ; lambertw_series(-exp(-1)+1e-50,epsilon(),0)-M -0.00000000000000000000000000000000000000002959444084 ; epsilon(1e-74) 0.00000000000000000000000000000000000000000000000000 ; lambertw_series(-exp(-1)+1e-50,epsilon(),0)-M -0.00000000000000000000000000000000000000000000000006 */ closeness = abs(z-branchpoint); if( closeness< 1){ if(closeness != 0) eps = epsilon(epsilon()*( closeness)); else eps = epsilon(epsilon()^2); } else eps = epsilon(epsilon()*1e-2); epslarge =epsilon(); places = highbit(1 + int(1/epslarge)) + 1; w = ret; for(k=0;k<100;k++){ ew = exp(w); we = w*ew; if(abs(we-z)<= 4*epslarge*abs(z))break; w1e = (1+w)*ew; wn = bround(w- ((we - z) / ( w1e - ( (w+2)*(we-z) )/(2*w+2) ) ),places++) ; if( abs(wn - w) <= epslarge*abs(wn)) break; else w = wn; } if(k==100){ epsilon(eps); return newerror("lambertw: Halley iteration does not converge"); } /* The Maxima coders added a check if the iteration converged to the correct branch. This coder deems it superfluous. */ epsilon(eps); return wn; } config("resource_debug", resource_debug_level),; if (config("resource_debug") & 3) { print "lambertw(z,branch)"; print "lambertw_series(z,eps,branch,terms)"; print "lambertw_series_print()"; }