Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Unified interfaces to root finding algorithms for real or complex 

3scalar functions. 

4 

5Functions 

6--------- 

7- root : find a root of a scalar function. 

8""" 

9import numpy as np 

10 

11from . import zeros as optzeros 

12 

13__all__ = ['root_scalar'] 

14 

15 

16class MemoizeDer(object): 

17 """Decorator that caches the value and derivative(s) of function each 

18 time it is called. 

19 

20 This is a simplistic memoizer that calls and caches a single value 

21 of `f(x, *args)`. 

22 It assumes that `args` does not change between invocations. 

23 It supports the use case of a root-finder where `args` is fixed, 

24 `x` changes, and only rarely, if at all, does x assume the same value 

25 more than once.""" 

26 def __init__(self, fun): 

27 self.fun = fun 

28 self.vals = None 

29 self.x = None 

30 self.n_calls = 0 

31 

32 def __call__(self, x, *args): 

33 r"""Calculate f or use cached value if available""" 

34 # Derivative may be requested before the function itself, always check 

35 if self.vals is None or x != self.x: 

36 fg = self.fun(x, *args) 

37 self.x = x 

38 self.n_calls += 1 

39 self.vals = fg[:] 

40 return self.vals[0] 

41 

42 def fprime(self, x, *args): 

43 r"""Calculate f' or use a cached value if available""" 

44 if self.vals is None or x != self.x: 

45 self(x, *args) 

46 return self.vals[1] 

47 

48 def fprime2(self, x, *args): 

49 r"""Calculate f'' or use a cached value if available""" 

50 if self.vals is None or x != self.x: 

51 self(x, *args) 

52 return self.vals[2] 

53 

54 def ncalls(self): 

55 return self.n_calls 

56 

57 

58def root_scalar(f, args=(), method=None, bracket=None, 

59 fprime=None, fprime2=None, 

60 x0=None, x1=None, 

61 xtol=None, rtol=None, maxiter=None, 

62 options=None): 

63 """ 

64 Find a root of a scalar function. 

65 

66 Parameters 

67 ---------- 

68 f : callable 

69 A function to find a root of. 

70 args : tuple, optional 

71 Extra arguments passed to the objective function and its derivative(s). 

72 method : str, optional 

73 Type of solver. Should be one of 

74 

75 - 'bisect' :ref:`(see here) <optimize.root_scalar-bisect>` 

76 - 'brentq' :ref:`(see here) <optimize.root_scalar-brentq>` 

77 - 'brenth' :ref:`(see here) <optimize.root_scalar-brenth>` 

78 - 'ridder' :ref:`(see here) <optimize.root_scalar-ridder>` 

79 - 'toms748' :ref:`(see here) <optimize.root_scalar-toms748>` 

80 - 'newton' :ref:`(see here) <optimize.root_scalar-newton>` 

81 - 'secant' :ref:`(see here) <optimize.root_scalar-secant>` 

82 - 'halley' :ref:`(see here) <optimize.root_scalar-halley>` 

83 

84 bracket: A sequence of 2 floats, optional 

85 An interval bracketing a root. `f(x, *args)` must have different 

86 signs at the two endpoints. 

87 x0 : float, optional 

88 Initial guess. 

89 x1 : float, optional 

90 A second guess. 

91 fprime : bool or callable, optional 

92 If `fprime` is a boolean and is True, `f` is assumed to return the 

93 value of the objective function and of the derivative. 

94 `fprime` can also be a callable returning the derivative of `f`. In 

95 this case, it must accept the same arguments as `f`. 

96 fprime2 : bool or callable, optional 

97 If `fprime2` is a boolean and is True, `f` is assumed to return the 

98 value of the objective function and of the 

99 first and second derivatives. 

100 `fprime2` can also be a callable returning the second derivative of `f`. 

101 In this case, it must accept the same arguments as `f`. 

102 xtol : float, optional 

103 Tolerance (absolute) for termination. 

104 rtol : float, optional 

105 Tolerance (relative) for termination. 

106 maxiter : int, optional 

107 Maximum number of iterations. 

108 options : dict, optional 

109 A dictionary of solver options. E.g., ``k``, see 

110 :obj:`show_options()` for details. 

111 

112 Returns 

113 ------- 

114 sol : RootResults 

115 The solution represented as a ``RootResults`` object. 

116 Important attributes are: ``root`` the solution , ``converged`` a 

117 boolean flag indicating if the algorithm exited successfully and 

118 ``flag`` which describes the cause of the termination. See 

119 `RootResults` for a description of other attributes. 

120 

121 See also 

122 -------- 

123 show_options : Additional options accepted by the solvers 

124 root : Find a root of a vector function. 

125 

126 Notes 

127 ----- 

128 This section describes the available solvers that can be selected by the 

129 'method' parameter. 

130 

131 The default is to use the best method available for the situation 

132 presented. 

133 If a bracket is provided, it may use one of the bracketing methods. 

134 If a derivative and an initial value are specified, it may 

135 select one of the derivative-based methods. 

136 If no method is judged applicable, it will raise an Exception. 

137 

138 

139 Examples 

140 -------- 

141 

142 Find the root of a simple cubic 

143 

144 >>> from scipy import optimize 

145 >>> def f(x): 

146 ... return (x**3 - 1) # only one real root at x = 1 

147 

148 >>> def fprime(x): 

149 ... return 3*x**2 

150 

151 The `brentq` method takes as input a bracket 

152 

153 >>> sol = optimize.root_scalar(f, bracket=[0, 3], method='brentq') 

154 >>> sol.root, sol.iterations, sol.function_calls 

155 (1.0, 10, 11) 

156 

157 The `newton` method takes as input a single point and uses the derivative(s) 

158 

159 >>> sol = optimize.root_scalar(f, x0=0.2, fprime=fprime, method='newton') 

160 >>> sol.root, sol.iterations, sol.function_calls 

161 (1.0, 11, 22) 

162 

163 The function can provide the value and derivative(s) in a single call. 

164 

165 >>> def f_p_pp(x): 

166 ... return (x**3 - 1), 3*x**2, 6*x 

167 

168 >>> sol = optimize.root_scalar(f_p_pp, x0=0.2, fprime=True, method='newton') 

169 >>> sol.root, sol.iterations, sol.function_calls 

170 (1.0, 11, 11) 

171 

172 >>> sol = optimize.root_scalar(f_p_pp, x0=0.2, fprime=True, fprime2=True, method='halley') 

173 >>> sol.root, sol.iterations, sol.function_calls 

174 (1.0, 7, 8) 

175 

176 

177 """ 

178 if not isinstance(args, tuple): 

179 args = (args,) 

180 

181 if options is None: 

182 options = {} 

183 

184 # fun also returns the derivative(s) 

185 is_memoized = False 

186 if fprime2 is not None and not callable(fprime2): 

187 if bool(fprime2): 

188 f = MemoizeDer(f) 

189 is_memoized = True 

190 fprime2 = f.fprime2 

191 fprime = f.fprime 

192 else: 

193 fprime2 = None 

194 if fprime is not None and not callable(fprime): 

195 if bool(fprime): 

196 f = MemoizeDer(f) 

197 is_memoized = True 

198 fprime = f.fprime 

199 else: 

200 fprime = None 

201 

202 # respect solver-specific default tolerances - only pass in if actually set 

203 kwargs = {} 

204 for k in ['xtol', 'rtol', 'maxiter']: 

205 v = locals().get(k) 

206 if v is not None: 

207 kwargs[k] = v 

208 

209 # Set any solver-specific options 

210 if options: 

211 kwargs.update(options) 

212 # Always request full_output from the underlying method as _root_scalar 

213 # always returns a RootResults object 

214 kwargs.update(full_output=True, disp=False) 

215 

216 # Pick a method if not specified. 

217 # Use the "best" method available for the situation. 

218 if not method: 

219 if bracket: 

220 method = 'brentq' 

221 elif x0 is not None: 

222 if fprime: 

223 if fprime2: 

224 method = 'halley' 

225 else: 

226 method = 'newton' 

227 else: 

228 method = 'secant' 

229 if not method: 

230 raise ValueError('Unable to select a solver as neither bracket ' 

231 'nor starting point provided.') 

232 

233 meth = method.lower() 

234 map2underlying = {'halley': 'newton', 'secant': 'newton'} 

235 

236 try: 

237 methodc = getattr(optzeros, map2underlying.get(meth, meth)) 

238 except AttributeError: 

239 raise ValueError('Unknown solver %s' % meth) 

240 

241 if meth in ['bisect', 'ridder', 'brentq', 'brenth', 'toms748']: 

242 if not isinstance(bracket, (list, tuple, np.ndarray)): 

243 raise ValueError('Bracket needed for %s' % method) 

244 

245 a, b = bracket[:2] 

246 r, sol = methodc(f, a, b, args=args, **kwargs) 

247 elif meth in ['secant']: 

248 if x0 is None: 

249 raise ValueError('x0 must not be None for %s' % method) 

250 if x1 is None: 

251 raise ValueError('x1 must not be None for %s' % method) 

252 if 'xtol' in kwargs: 

253 kwargs['tol'] = kwargs.pop('xtol') 

254 r, sol = methodc(f, x0, args=args, fprime=None, fprime2=None, 

255 x1=x1, **kwargs) 

256 elif meth in ['newton']: 

257 if x0 is None: 

258 raise ValueError('x0 must not be None for %s' % method) 

259 if not fprime: 

260 raise ValueError('fprime must be specified for %s' % method) 

261 if 'xtol' in kwargs: 

262 kwargs['tol'] = kwargs.pop('xtol') 

263 r, sol = methodc(f, x0, args=args, fprime=fprime, fprime2=None, 

264 **kwargs) 

265 elif meth in ['halley']: 

266 if x0 is None: 

267 raise ValueError('x0 must not be None for %s' % method) 

268 if not fprime: 

269 raise ValueError('fprime must be specified for %s' % method) 

270 if not fprime2: 

271 raise ValueError('fprime2 must be specified for %s' % method) 

272 if 'xtol' in kwargs: 

273 kwargs['tol'] = kwargs.pop('xtol') 

274 r, sol = methodc(f, x0, args=args, fprime=fprime, fprime2=fprime2, **kwargs) 

275 else: 

276 raise ValueError('Unknown solver %s' % method) 

277 

278 if is_memoized: 

279 # Replace the function_calls count with the memoized count. 

280 # Avoids double and triple-counting. 

281 n_calls = f.n_calls 

282 sol.function_calls = n_calls 

283 

284 return sol 

285 

286 

287def _root_scalar_brentq_doc(): 

288 r""" 

289 Options 

290 ------- 

291 args : tuple, optional 

292 Extra arguments passed to the objective function. 

293 xtol : float, optional 

294 Tolerance (absolute) for termination. 

295 rtol : float, optional 

296 Tolerance (relative) for termination. 

297 maxiter : int, optional 

298 Maximum number of iterations. 

299 options: dict, optional 

300 Specifies any method-specific options not covered above 

301 

302 """ 

303 pass 

304 

305 

306def _root_scalar_brenth_doc(): 

307 r""" 

308 Options 

309 ------- 

310 args : tuple, optional 

311 Extra arguments passed to the objective function. 

312 xtol : float, optional 

313 Tolerance (absolute) for termination. 

314 rtol : float, optional 

315 Tolerance (relative) for termination. 

316 maxiter : int, optional 

317 Maximum number of iterations. 

318 options: dict, optional 

319 Specifies any method-specific options not covered above. 

320 

321 """ 

322 pass 

323 

324def _root_scalar_toms748_doc(): 

325 r""" 

326 Options 

327 ------- 

328 args : tuple, optional 

329 Extra arguments passed to the objective function. 

330 xtol : float, optional 

331 Tolerance (absolute) for termination. 

332 rtol : float, optional 

333 Tolerance (relative) for termination. 

334 maxiter : int, optional 

335 Maximum number of iterations. 

336 options: dict, optional 

337 Specifies any method-specific options not covered above. 

338 

339 """ 

340 pass 

341 

342 

343def _root_scalar_secant_doc(): 

344 r""" 

345 Options 

346 ------- 

347 args : tuple, optional 

348 Extra arguments passed to the objective function. 

349 xtol : float, optional 

350 Tolerance (absolute) for termination. 

351 rtol : float, optional 

352 Tolerance (relative) for termination. 

353 maxiter : int, optional 

354 Maximum number of iterations. 

355 x0 : float, required 

356 Initial guess. 

357 x1 : float, required 

358 A second guess. 

359 options: dict, optional 

360 Specifies any method-specific options not covered above. 

361 

362 """ 

363 pass 

364 

365 

366def _root_scalar_newton_doc(): 

367 r""" 

368 Options 

369 ------- 

370 args : tuple, optional 

371 Extra arguments passed to the objective function and its derivative. 

372 xtol : float, optional 

373 Tolerance (absolute) for termination. 

374 rtol : float, optional 

375 Tolerance (relative) for termination. 

376 maxiter : int, optional 

377 Maximum number of iterations. 

378 x0 : float, required 

379 Initial guess. 

380 fprime : bool or callable, optional 

381 If `fprime` is a boolean and is True, `f` is assumed to return the 

382 value of derivative along with the objective function. 

383 `fprime` can also be a callable returning the derivative of `f`. In 

384 this case, it must accept the same arguments as `f`. 

385 options: dict, optional 

386 Specifies any method-specific options not covered above. 

387 

388 """ 

389 pass 

390 

391 

392def _root_scalar_halley_doc(): 

393 r""" 

394 Options 

395 ------- 

396 args : tuple, optional 

397 Extra arguments passed to the objective function and its derivatives. 

398 xtol : float, optional 

399 Tolerance (absolute) for termination. 

400 rtol : float, optional 

401 Tolerance (relative) for termination. 

402 maxiter : int, optional 

403 Maximum number of iterations. 

404 x0 : float, required 

405 Initial guess. 

406 fprime : bool or callable, required 

407 If `fprime` is a boolean and is True, `f` is assumed to return the 

408 value of derivative along with the objective function. 

409 `fprime` can also be a callable returning the derivative of `f`. In 

410 this case, it must accept the same arguments as `f`. 

411 fprime2 : bool or callable, required 

412 If `fprime2` is a boolean and is True, `f` is assumed to return the 

413 value of 1st and 2nd derivatives along with the objective function. 

414 `fprime2` can also be a callable returning the 2nd derivative of `f`. 

415 In this case, it must accept the same arguments as `f`. 

416 options: dict, optional 

417 Specifies any method-specific options not covered above. 

418 

419 """ 

420 pass 

421 

422 

423def _root_scalar_ridder_doc(): 

424 r""" 

425 Options 

426 ------- 

427 args : tuple, optional 

428 Extra arguments passed to the objective function. 

429 xtol : float, optional 

430 Tolerance (absolute) for termination. 

431 rtol : float, optional 

432 Tolerance (relative) for termination. 

433 maxiter : int, optional 

434 Maximum number of iterations. 

435 options: dict, optional 

436 Specifies any method-specific options not covered above. 

437 

438 """ 

439 pass 

440 

441 

442def _root_scalar_bisect_doc(): 

443 r""" 

444 Options 

445 ------- 

446 args : tuple, optional 

447 Extra arguments passed to the objective function. 

448 xtol : float, optional 

449 Tolerance (absolute) for termination. 

450 rtol : float, optional 

451 Tolerance (relative) for termination. 

452 maxiter : int, optional 

453 Maximum number of iterations. 

454 options: dict, optional 

455 Specifies any method-specific options not covered above. 

456 

457 """ 

458 pass