solve f(x)==value very efficiently, with explicit knowledge of derivatives of the function find_root solves by iterated inverse quadratic extrapolation for a solution to f(x)=y. It includes checks against bad convergence, so it should never be able to fail. Unlike typical secant method or fancier Brent's method finders, this does not depend in any strong wasy on the brackets, unless the finder has to resort to successive approximations to close in on a root. Often, it is possible to make the brackets equal to the domain of the function, if there is any clue as to where the root lies, as given by the parameter start.
Definition at line 157 of file C2Functions.py. 00157 : 00158 "solve f(x)==value very efficiently, with explicit knowledge of derivatives of the function" 00159 # can use local f(x)=a*x**2 + b*x + c 00160 # and solve quadratic to find root, then iterate 00161 00162 ftol=(5e-16*abs(value)+2e-308); 00163 xtol=(5e-16*(abs(upper_bracket)+abs(lower_bracket))+2e-308); 00164 00165 root=start #start looking in the middle 00166 cupper, yp, ypp=self.value_with_derivatives(upper_bracket) 00167 cupper -= value 00168 if abs(cupper) < ftol: return upper_bracket 00169 clower, yp, ypp=self.value_with_derivatives(lower_bracket) 00170 clower -= value 00171 if abs(clower) < ftol: return lower_bracket; 00172 00173 if cupper*clower >0: 00174 # argh, no sign change in here! 00175 raise C2Exception("unbracketed root in find_root at xlower=%g, xupper=%g, bailing" % 00176 (lower_bracket, upper_bracket)) 00177 00178 delta=upper_bracket-lower_bracket #first error step 00179 c, b, ypp=self.value_with_derivatives(root) # compute initial values 00180 c -= value 00181 00182 while abs(delta) > xtol: # can allow quite small steps, since we have exact derivatives! 00183 a=ypp/2 #second derivative is 2*a 00184 disc=b*b-4*a*c 00185 if disc >= 0: 00186 if b>=0: 00187 q=-0.5*(b+math.sqrt(disc)) 00188 else: 00189 q=-0.5*(b-math.sqrt(disc)) 00190 00191 if q*q > abs(a*c): 00192 delta=c/q #since x1=q/a, x2=c/q, x1/x2=q^2/ac, this picks smaller step 00193 else: delta=q/a 00194 root+=delta; 00195 00196 if disc < 0 or root<=lower_bracket or root>=upper_bracket or abs(delta) >= 0.5*(upper_bracket-lower_bracket): 00197 #if we jump out of the bracket, or if the step is not smaller than half the bracket, bisect 00198 root=0.5*(lower_bracket+upper_bracket) 00199 delta=upper_bracket-lower_bracket 00200 00201 c, b, ypp=self.value_with_derivatives(root) #get local curvature and value 00202 c -= value 00203 00204 if trace: 00205 import sys 00206 print >> sys.stderr, "find_root x, dx, c, b, a", ( (5*"%10.4g ") % 00207 (root, delta, c, b, ypp/2) ) 00208 00209 if abs(c) < ftol or abs(c) < xtol*abs(b): 00210 return root #got it close enough 00211 00212 # now, close in bracket on whichever side this still brackets 00213 if c*clower < 0.0: 00214 cupper=c 00215 upper_bracket=root 00216 else: 00217 clower=c 00218 lower_bracket=root 00219 00220 return root 00221
|