Path: csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!gegeweb.org!de-l.enfer-du-nord.net!feeder1.enfer-du-nord.net!newsfeed.eweka.nl!eweka.nl!feeder3.eweka.nl!newsfeed.xs4all.nl!newsfeed5.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'else:': 0.03; 'subject:Python': 0.05; '"""': 0.07; 'calculating': 0.07; 'function,': 0.07; 'received:verizon.net': 0.07; 'something,': 0.07; 'terry': 0.07; 'python': 0.08; 'assert': 0.09; 'delta': 0.09; 'function:': 0.09; 'investigate': 0.09; 'mentions': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'am,': 0.12; 'def': 0.13; 'operations,': 0.15; '"""use': 0.16; 'bouncing': 0.16; 'enough?': 0.16; 'err': 0.16; 'intrinsic': 0.16; 'otoh,': 0.16; 'reedy': 0.16; 'skip:8 20': 0.16; 'thereto': 0.16; 'values:': 0.16; 'wrote:': 0.18; 'this?': 0.19; 'jan': 0.19; "haven't": 0.20; '(most': 0.21; 'trying': 0.21; 'maybe': 0.21; 'discussion': 0.22; 'header:In-Reply-To:1': 0.22; 'feb': 0.22; 'numeric': 0.23; 'math': 0.24; 'traceback': 0.24; 'performing': 0.26; 'function': 0.27; 'import': 0.27; 'looks': 0.27; 'raise': 0.28; 'bit': 0.28; "i'm": 0.28; 'example': 0.29; 'problem': 0.29; 'print': 0.29; 'correct': 0.29; 'fairly': 0.30; '(the': 0.30; 'error': 0.30; 'anyone': 0.32; 'does': 0.32; 'break': 0.32; 'initial': 0.32; 'there': 0.33; 'this.': 0.33; 'header:User- Agent:1': 0.33; 'it.': 0.33; 'file': 0.34; 'header:X-Complaints- To:1': 0.34; 'mostly': 0.34; 'steven': 0.34; 'rather': 0.34; 'last):': 0.34; 'routine': 0.34; 'stuck': 0.34; 'try:': 0.34; 'operations': 0.35; 'to:addr:python-list': 0.35; 'none': 0.35; 'something': 0.35; 'be.': 0.35; 'apply': 0.35; 'two': 0.36; 'received:org': 0.36; 'with.': 0.37; 'but': 0.37; 'sometimes': 0.38; 'except': 0.39; 'point': 0.40; 'to:addr:python.org': 0.40; 'back': 0.60; 'your': 0.61; 'subject': 0.61; 'improve': 0.62; 'kind': 0.62; 'limit': 0.67; '19,': 0.68; 'skip:2 20': 0.71; 'papers': 0.74; 'forth': 0.77; 'iterative': 0.84; 'one).': 0.84; 'tricky': 0.84; 'suspected': 0.93; 'technique': 0.93 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Terry Reedy Subject: Re: Numeric root-finding in Python Date: Sun, 12 Feb 2012 16:19:31 -0500 References: <4f375f0f$0$29986$c3e8da3$5496439d@news.astraweb.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Gmane-NNTP-Posting-Host: pool-74-109-121-73.phlapa.fios.verizon.net User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0) Gecko/20111105 Thunderbird/8.0 In-Reply-To: X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 90 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1329081584 news.xs4all.nl 6891 [2001:888:2000:d::a6]:54215 X-Complaints-To: abuse@xs4all.nl Xref: x330-a1.tempe.blueboxinc.net comp.lang.python:20295 On 2/12/2012 5:10 AM, Eelco wrote: > On Feb 12, 7:41 am, Steven D'Aprano +comp.lang.pyt...@pearwood.info> wrote: >> This is only peripherally a Python problem, but in case anyone has any >> good ideas I'm going to ask it. >> >> I have a routine to calculate an approximation of Lambert's W function, >> and then apply a root-finding technique to improve the approximation. >> This mostly works well, but sometimes the root-finder gets stuck in a >> cycle. >> >> Here's my function: >> >> import math >> def improve(x, w, exp=math.exp): >> """Use Halley's method to improve an estimate of W(x) given >> an initial estimate w. >> """ >> try: >> for i in range(36): # Max number of iterations. >> ew = exp(w) >> a = w*ew - x >> b = ew*(w + 1) >> err = -a/b # Estimate of the error in the current w. >> if abs(err)<= 1e-16: >> break >> print '%d: w= %r err= %r' % (i, w, err) >> # Make a better estimate. >> c = (w + 2)*a/(2*w + 2) >> delta = a/(b - c) >> w -= delta >> else: >> raise RuntimeError('calculation failed to converge', err) >> except ZeroDivisionError: >> assert w == -1 >> return w >> >> Here's an example where improve() converges very quickly: >> >> py> improve(-0.36, -1.222769842388856) >> 0: w= -1.222769842388856 err= -2.9158979924038895e-07 >> 1: w= -1.2227701339785069 err= 8.4638038491998997e-16 >> -1.222770133978506 >> >> That's what I expect: convergence in only a few iterations. >> >> Here's an example where it gets stuck in a cycle, bouncing back and forth >> between two values: >> >> py> improve(-0.36787344117144249, -1.0057222396915309) >> 0: w= -1.0057222396915309 err= 2.6521238905750239e-14 >> 1: w= -1.0057222396915044 err= -2.6521238905872001e-14 >> 2: w= -1.0057222396915309 err= 2.6521238905750239e-14 >> 3: w= -1.0057222396915044 err= -2.6521238905872001e-14 >> 4: w= -1.0057222396915309 err= 2.6521238905750239e-14 >> 35: w= -1.0057222396915044 err= -2.6521238905872001e-14 >> Traceback (most recent call last): >> File "", line 1, in >> File "", line 19, in improve >> RuntimeError: ('calculation failed to converge', -2.6521238905872001e-14) >> >> (The correct value for w is approximately -1.00572223991.) >> >> I know that Newton's method is subject to cycles, but I haven't found any >> discussion about Halley's method and cycles, nor do I know what the best >> approach for breaking them would be. None of the papers on calculating >> the Lambert W function that I have found mentions this. >> >> Does anyone have any advice for solving this? > Looks like floating point issues to me, rather than something > intrinsic to the iterative algorithm. Surely there is not complex > chaotic behavior to be found in this fairly smooth function in a +/- > 1e-14 window. Otoh, there is a lot of floating point significant bit > loss issues to be suspected in the kind of operations you are > performing (exp(x) + something, always a tricky one). To investigate this, I would limit the iterations to 2 or 3 and print ew, a,b,c, and delta, maybe in binary(hex) form > I would start by asking: How accurate is good enough? If its not good > enough, play around the the ordering of your operations, try solving a > transformed problem less sensitive to loss of significance; and begin > by trying different numeric types to see if the problem is sensitive > thereto to begin with. -- Terry Jan Reedy