Coverage for /usr/lib/python3/dist-packages/scipy/optimize/_linesearch.py: 6%

305 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2Functions 

3--------- 

4.. autosummary:: 

5 :toctree: generated/ 

6 

7 line_search_armijo 

8 line_search_wolfe1 

9 line_search_wolfe2 

10 scalar_search_wolfe1 

11 scalar_search_wolfe2 

12 

13""" 

14from warnings import warn 

15 

16from scipy.optimize import _minpack2 as minpack2 

17import numpy as np 

18 

19__all__ = ['LineSearchWarning', 'line_search_wolfe1', 'line_search_wolfe2', 

20 'scalar_search_wolfe1', 'scalar_search_wolfe2', 

21 'line_search_armijo'] 

22 

23class LineSearchWarning(RuntimeWarning): 

24 pass 

25 

26 

27#------------------------------------------------------------------------------ 

28# Minpack's Wolfe line and scalar searches 

29#------------------------------------------------------------------------------ 

30 

31def line_search_wolfe1(f, fprime, xk, pk, gfk=None, 

32 old_fval=None, old_old_fval=None, 

33 args=(), c1=1e-4, c2=0.9, amax=50, amin=1e-8, 

34 xtol=1e-14): 

35 """ 

36 As `scalar_search_wolfe1` but do a line search to direction `pk` 

37 

38 Parameters 

39 ---------- 

40 f : callable 

41 Function `f(x)` 

42 fprime : callable 

43 Gradient of `f` 

44 xk : array_like 

45 Current point 

46 pk : array_like 

47 Search direction 

48 

49 gfk : array_like, optional 

50 Gradient of `f` at point `xk` 

51 old_fval : float, optional 

52 Value of `f` at point `xk` 

53 old_old_fval : float, optional 

54 Value of `f` at point preceding `xk` 

55 

56 The rest of the parameters are the same as for `scalar_search_wolfe1`. 

57 

58 Returns 

59 ------- 

60 stp, f_count, g_count, fval, old_fval 

61 As in `line_search_wolfe1` 

62 gval : array 

63 Gradient of `f` at the final point 

64 

65 """ 

66 if gfk is None: 

67 gfk = fprime(xk, *args) 

68 

69 gval = [gfk] 

70 gc = [0] 

71 fc = [0] 

72 

73 def phi(s): 

74 fc[0] += 1 

75 return f(xk + s*pk, *args) 

76 

77 def derphi(s): 

78 gval[0] = fprime(xk + s*pk, *args) 

79 gc[0] += 1 

80 return np.dot(gval[0], pk) 

81 

82 derphi0 = np.dot(gfk, pk) 

83 

84 stp, fval, old_fval = scalar_search_wolfe1( 

85 phi, derphi, old_fval, old_old_fval, derphi0, 

86 c1=c1, c2=c2, amax=amax, amin=amin, xtol=xtol) 

87 

88 return stp, fc[0], gc[0], fval, old_fval, gval[0] 

89 

90 

91def scalar_search_wolfe1(phi, derphi, phi0=None, old_phi0=None, derphi0=None, 

92 c1=1e-4, c2=0.9, 

93 amax=50, amin=1e-8, xtol=1e-14): 

94 """ 

95 Scalar function search for alpha that satisfies strong Wolfe conditions 

96 

97 alpha > 0 is assumed to be a descent direction. 

98 

99 Parameters 

100 ---------- 

101 phi : callable phi(alpha) 

102 Function at point `alpha` 

103 derphi : callable phi'(alpha) 

104 Objective function derivative. Returns a scalar. 

105 phi0 : float, optional 

106 Value of phi at 0 

107 old_phi0 : float, optional 

108 Value of phi at previous point 

109 derphi0 : float, optional 

110 Value derphi at 0 

111 c1 : float, optional 

112 Parameter for Armijo condition rule. 

113 c2 : float, optional 

114 Parameter for curvature condition rule. 

115 amax, amin : float, optional 

116 Maximum and minimum step size 

117 xtol : float, optional 

118 Relative tolerance for an acceptable step. 

119 

120 Returns 

121 ------- 

122 alpha : float 

123 Step size, or None if no suitable step was found 

124 phi : float 

125 Value of `phi` at the new point `alpha` 

126 phi0 : float 

127 Value of `phi` at `alpha=0` 

128 

129 Notes 

130 ----- 

131 Uses routine DCSRCH from MINPACK. 

132 

133 """ 

134 

135 if phi0 is None: 

136 phi0 = phi(0.) 

137 if derphi0 is None: 

138 derphi0 = derphi(0.) 

139 

140 if old_phi0 is not None and derphi0 != 0: 

141 alpha1 = min(1.0, 1.01*2*(phi0 - old_phi0)/derphi0) 

142 if alpha1 < 0: 

143 alpha1 = 1.0 

144 else: 

145 alpha1 = 1.0 

146 

147 phi1 = phi0 

148 derphi1 = derphi0 

149 isave = np.zeros((2,), np.intc) 

150 dsave = np.zeros((13,), float) 

151 task = b'START' 

152 

153 maxiter = 100 

154 for i in range(maxiter): 

155 stp, phi1, derphi1, task = minpack2.dcsrch(alpha1, phi1, derphi1, 

156 c1, c2, xtol, task, 

157 amin, amax, isave, dsave) 

158 if task[:2] == b'FG': 

159 alpha1 = stp 

160 phi1 = phi(stp) 

161 derphi1 = derphi(stp) 

162 else: 

163 break 

164 else: 

165 # maxiter reached, the line search did not converge 

166 stp = None 

167 

168 if task[:5] == b'ERROR' or task[:4] == b'WARN': 

169 stp = None # failed 

170 

171 return stp, phi1, phi0 

172 

173 

174line_search = line_search_wolfe1 

175 

176 

177#------------------------------------------------------------------------------ 

178# Pure-Python Wolfe line and scalar searches 

179#------------------------------------------------------------------------------ 

180 

181# Note: `line_search_wolfe2` is the public `scipy.optimize.line_search` 

182 

183def line_search_wolfe2(f, myfprime, xk, pk, gfk=None, old_fval=None, 

184 old_old_fval=None, args=(), c1=1e-4, c2=0.9, amax=None, 

185 extra_condition=None, maxiter=10): 

186 """Find alpha that satisfies strong Wolfe conditions. 

187 

188 Parameters 

189 ---------- 

190 f : callable f(x,*args) 

191 Objective function. 

192 myfprime : callable f'(x,*args) 

193 Objective function gradient. 

194 xk : ndarray 

195 Starting point. 

196 pk : ndarray 

197 Search direction. The search direction must be a descent direction 

198 for the algorithm to converge. 

199 gfk : ndarray, optional 

200 Gradient value for x=xk (xk being the current parameter 

201 estimate). Will be recomputed if omitted. 

202 old_fval : float, optional 

203 Function value for x=xk. Will be recomputed if omitted. 

204 old_old_fval : float, optional 

205 Function value for the point preceding x=xk. 

206 args : tuple, optional 

207 Additional arguments passed to objective function. 

208 c1 : float, optional 

209 Parameter for Armijo condition rule. 

210 c2 : float, optional 

211 Parameter for curvature condition rule. 

212 amax : float, optional 

213 Maximum step size 

214 extra_condition : callable, optional 

215 A callable of the form ``extra_condition(alpha, x, f, g)`` 

216 returning a boolean. Arguments are the proposed step ``alpha`` 

217 and the corresponding ``x``, ``f`` and ``g`` values. The line search 

218 accepts the value of ``alpha`` only if this 

219 callable returns ``True``. If the callable returns ``False`` 

220 for the step length, the algorithm will continue with 

221 new iterates. The callable is only called for iterates 

222 satisfying the strong Wolfe conditions. 

223 maxiter : int, optional 

224 Maximum number of iterations to perform. 

225 

226 Returns 

227 ------- 

228 alpha : float or None 

229 Alpha for which ``x_new = x0 + alpha * pk``, 

230 or None if the line search algorithm did not converge. 

231 fc : int 

232 Number of function evaluations made. 

233 gc : int 

234 Number of gradient evaluations made. 

235 new_fval : float or None 

236 New function value ``f(x_new)=f(x0+alpha*pk)``, 

237 or None if the line search algorithm did not converge. 

238 old_fval : float 

239 Old function value ``f(x0)``. 

240 new_slope : float or None 

241 The local slope along the search direction at the 

242 new value ``<myfprime(x_new), pk>``, 

243 or None if the line search algorithm did not converge. 

244 

245 

246 Notes 

247 ----- 

248 Uses the line search algorithm to enforce strong Wolfe 

249 conditions. See Wright and Nocedal, 'Numerical Optimization', 

250 1999, pp. 59-61. 

251 

252 The search direction `pk` must be a descent direction (e.g. 

253 ``-myfprime(xk)``) to find a step length that satisfies the strong Wolfe 

254 conditions. If the search direction is not a descent direction (e.g. 

255 ``myfprime(xk)``), then `alpha`, `new_fval`, and `new_slope` will be None. 

256 

257 Examples 

258 -------- 

259 >>> import numpy as np 

260 >>> from scipy.optimize import line_search 

261 

262 A objective function and its gradient are defined. 

263 

264 >>> def obj_func(x): 

265 ... return (x[0])**2+(x[1])**2 

266 >>> def obj_grad(x): 

267 ... return [2*x[0], 2*x[1]] 

268 

269 We can find alpha that satisfies strong Wolfe conditions. 

270 

271 >>> start_point = np.array([1.8, 1.7]) 

272 >>> search_gradient = np.array([-1.0, -1.0]) 

273 >>> line_search(obj_func, obj_grad, start_point, search_gradient) 

274 (1.0, 2, 1, 1.1300000000000001, 6.13, [1.6, 1.4]) 

275 

276 """ 

277 fc = [0] 

278 gc = [0] 

279 gval = [None] 

280 gval_alpha = [None] 

281 

282 def phi(alpha): 

283 fc[0] += 1 

284 return f(xk + alpha * pk, *args) 

285 

286 fprime = myfprime 

287 

288 def derphi(alpha): 

289 gc[0] += 1 

290 gval[0] = fprime(xk + alpha * pk, *args) # store for later use 

291 gval_alpha[0] = alpha 

292 return np.dot(gval[0], pk) 

293 

294 if gfk is None: 

295 gfk = fprime(xk, *args) 

296 derphi0 = np.dot(gfk, pk) 

297 

298 if extra_condition is not None: 

299 # Add the current gradient as argument, to avoid needless 

300 # re-evaluation 

301 def extra_condition2(alpha, phi): 

302 if gval_alpha[0] != alpha: 

303 derphi(alpha) 

304 x = xk + alpha * pk 

305 return extra_condition(alpha, x, phi, gval[0]) 

306 else: 

307 extra_condition2 = None 

308 

309 alpha_star, phi_star, old_fval, derphi_star = scalar_search_wolfe2( 

310 phi, derphi, old_fval, old_old_fval, derphi0, c1, c2, amax, 

311 extra_condition2, maxiter=maxiter) 

312 

313 if derphi_star is None: 

314 warn('The line search algorithm did not converge', LineSearchWarning) 

315 else: 

316 # derphi_star is a number (derphi) -- so use the most recently 

317 # calculated gradient used in computing it derphi = gfk*pk 

318 # this is the gradient at the next step no need to compute it 

319 # again in the outer loop. 

320 derphi_star = gval[0] 

321 

322 return alpha_star, fc[0], gc[0], phi_star, old_fval, derphi_star 

323 

324 

325def scalar_search_wolfe2(phi, derphi, phi0=None, 

326 old_phi0=None, derphi0=None, 

327 c1=1e-4, c2=0.9, amax=None, 

328 extra_condition=None, maxiter=10): 

329 """Find alpha that satisfies strong Wolfe conditions. 

330 

331 alpha > 0 is assumed to be a descent direction. 

332 

333 Parameters 

334 ---------- 

335 phi : callable phi(alpha) 

336 Objective scalar function. 

337 derphi : callable phi'(alpha) 

338 Objective function derivative. Returns a scalar. 

339 phi0 : float, optional 

340 Value of phi at 0. 

341 old_phi0 : float, optional 

342 Value of phi at previous point. 

343 derphi0 : float, optional 

344 Value of derphi at 0 

345 c1 : float, optional 

346 Parameter for Armijo condition rule. 

347 c2 : float, optional 

348 Parameter for curvature condition rule. 

349 amax : float, optional 

350 Maximum step size. 

351 extra_condition : callable, optional 

352 A callable of the form ``extra_condition(alpha, phi_value)`` 

353 returning a boolean. The line search accepts the value 

354 of ``alpha`` only if this callable returns ``True``. 

355 If the callable returns ``False`` for the step length, 

356 the algorithm will continue with new iterates. 

357 The callable is only called for iterates satisfying 

358 the strong Wolfe conditions. 

359 maxiter : int, optional 

360 Maximum number of iterations to perform. 

361 

362 Returns 

363 ------- 

364 alpha_star : float or None 

365 Best alpha, or None if the line search algorithm did not converge. 

366 phi_star : float 

367 phi at alpha_star. 

368 phi0 : float 

369 phi at 0. 

370 derphi_star : float or None 

371 derphi at alpha_star, or None if the line search algorithm 

372 did not converge. 

373 

374 Notes 

375 ----- 

376 Uses the line search algorithm to enforce strong Wolfe 

377 conditions. See Wright and Nocedal, 'Numerical Optimization', 

378 1999, pp. 59-61. 

379 

380 """ 

381 

382 if phi0 is None: 

383 phi0 = phi(0.) 

384 

385 if derphi0 is None: 

386 derphi0 = derphi(0.) 

387 

388 alpha0 = 0 

389 if old_phi0 is not None and derphi0 != 0: 

390 alpha1 = min(1.0, 1.01*2*(phi0 - old_phi0)/derphi0) 

391 else: 

392 alpha1 = 1.0 

393 

394 if alpha1 < 0: 

395 alpha1 = 1.0 

396 

397 if amax is not None: 

398 alpha1 = min(alpha1, amax) 

399 

400 phi_a1 = phi(alpha1) 

401 #derphi_a1 = derphi(alpha1) evaluated below 

402 

403 phi_a0 = phi0 

404 derphi_a0 = derphi0 

405 

406 if extra_condition is None: 

407 def extra_condition(alpha, phi): 

408 return True 

409 

410 for i in range(maxiter): 

411 if alpha1 == 0 or (amax is not None and alpha0 == amax): 

412 # alpha1 == 0: This shouldn't happen. Perhaps the increment has 

413 # slipped below machine precision? 

414 alpha_star = None 

415 phi_star = phi0 

416 phi0 = old_phi0 

417 derphi_star = None 

418 

419 if alpha1 == 0: 

420 msg = 'Rounding errors prevent the line search from converging' 

421 else: 

422 msg = "The line search algorithm could not find a solution " + \ 

423 "less than or equal to amax: %s" % amax 

424 

425 warn(msg, LineSearchWarning) 

426 break 

427 

428 not_first_iteration = i > 0 

429 if (phi_a1 > phi0 + c1 * alpha1 * derphi0) or \ 

430 ((phi_a1 >= phi_a0) and not_first_iteration): 

431 alpha_star, phi_star, derphi_star = \ 

432 _zoom(alpha0, alpha1, phi_a0, 

433 phi_a1, derphi_a0, phi, derphi, 

434 phi0, derphi0, c1, c2, extra_condition) 

435 break 

436 

437 derphi_a1 = derphi(alpha1) 

438 if (abs(derphi_a1) <= -c2*derphi0): 

439 if extra_condition(alpha1, phi_a1): 

440 alpha_star = alpha1 

441 phi_star = phi_a1 

442 derphi_star = derphi_a1 

443 break 

444 

445 if (derphi_a1 >= 0): 

446 alpha_star, phi_star, derphi_star = \ 

447 _zoom(alpha1, alpha0, phi_a1, 

448 phi_a0, derphi_a1, phi, derphi, 

449 phi0, derphi0, c1, c2, extra_condition) 

450 break 

451 

452 alpha2 = 2 * alpha1 # increase by factor of two on each iteration 

453 if amax is not None: 

454 alpha2 = min(alpha2, amax) 

455 alpha0 = alpha1 

456 alpha1 = alpha2 

457 phi_a0 = phi_a1 

458 phi_a1 = phi(alpha1) 

459 derphi_a0 = derphi_a1 

460 

461 else: 

462 # stopping test maxiter reached 

463 alpha_star = alpha1 

464 phi_star = phi_a1 

465 derphi_star = None 

466 warn('The line search algorithm did not converge', LineSearchWarning) 

467 

468 return alpha_star, phi_star, phi0, derphi_star 

469 

470 

471def _cubicmin(a, fa, fpa, b, fb, c, fc): 

472 """ 

473 Finds the minimizer for a cubic polynomial that goes through the 

474 points (a,fa), (b,fb), and (c,fc) with derivative at a of fpa. 

475 

476 If no minimizer can be found, return None. 

477 

478 """ 

479 # f(x) = A *(x-a)^3 + B*(x-a)^2 + C*(x-a) + D 

480 

481 with np.errstate(divide='raise', over='raise', invalid='raise'): 

482 try: 

483 C = fpa 

484 db = b - a 

485 dc = c - a 

486 denom = (db * dc) ** 2 * (db - dc) 

487 d1 = np.empty((2, 2)) 

488 d1[0, 0] = dc ** 2 

489 d1[0, 1] = -db ** 2 

490 d1[1, 0] = -dc ** 3 

491 d1[1, 1] = db ** 3 

492 [A, B] = np.dot(d1, np.asarray([fb - fa - C * db, 

493 fc - fa - C * dc]).flatten()) 

494 A /= denom 

495 B /= denom 

496 radical = B * B - 3 * A * C 

497 xmin = a + (-B + np.sqrt(radical)) / (3 * A) 

498 except ArithmeticError: 

499 return None 

500 if not np.isfinite(xmin): 

501 return None 

502 return xmin 

503 

504 

505def _quadmin(a, fa, fpa, b, fb): 

506 """ 

507 Finds the minimizer for a quadratic polynomial that goes through 

508 the points (a,fa), (b,fb) with derivative at a of fpa. 

509 

510 """ 

511 # f(x) = B*(x-a)^2 + C*(x-a) + D 

512 with np.errstate(divide='raise', over='raise', invalid='raise'): 

513 try: 

514 D = fa 

515 C = fpa 

516 db = b - a * 1.0 

517 B = (fb - D - C * db) / (db * db) 

518 xmin = a - C / (2.0 * B) 

519 except ArithmeticError: 

520 return None 

521 if not np.isfinite(xmin): 

522 return None 

523 return xmin 

524 

525 

526def _zoom(a_lo, a_hi, phi_lo, phi_hi, derphi_lo, 

527 phi, derphi, phi0, derphi0, c1, c2, extra_condition): 

528 """Zoom stage of approximate linesearch satisfying strong Wolfe conditions. 

529 

530 Part of the optimization algorithm in `scalar_search_wolfe2`. 

531 

532 Notes 

533 ----- 

534 Implements Algorithm 3.6 (zoom) in Wright and Nocedal, 

535 'Numerical Optimization', 1999, pp. 61. 

536 

537 """ 

538 

539 maxiter = 10 

540 i = 0 

541 delta1 = 0.2 # cubic interpolant check 

542 delta2 = 0.1 # quadratic interpolant check 

543 phi_rec = phi0 

544 a_rec = 0 

545 while True: 

546 # interpolate to find a trial step length between a_lo and 

547 # a_hi Need to choose interpolation here. Use cubic 

548 # interpolation and then if the result is within delta * 

549 # dalpha or outside of the interval bounded by a_lo or a_hi 

550 # then use quadratic interpolation, if the result is still too 

551 # close, then use bisection 

552 

553 dalpha = a_hi - a_lo 

554 if dalpha < 0: 

555 a, b = a_hi, a_lo 

556 else: 

557 a, b = a_lo, a_hi 

558 

559 # minimizer of cubic interpolant 

560 # (uses phi_lo, derphi_lo, phi_hi, and the most recent value of phi) 

561 # 

562 # if the result is too close to the end points (or out of the 

563 # interval), then use quadratic interpolation with phi_lo, 

564 # derphi_lo and phi_hi if the result is still too close to the 

565 # end points (or out of the interval) then use bisection 

566 

567 if (i > 0): 

568 cchk = delta1 * dalpha 

569 a_j = _cubicmin(a_lo, phi_lo, derphi_lo, a_hi, phi_hi, 

570 a_rec, phi_rec) 

571 if (i == 0) or (a_j is None) or (a_j > b - cchk) or (a_j < a + cchk): 

572 qchk = delta2 * dalpha 

573 a_j = _quadmin(a_lo, phi_lo, derphi_lo, a_hi, phi_hi) 

574 if (a_j is None) or (a_j > b-qchk) or (a_j < a+qchk): 

575 a_j = a_lo + 0.5*dalpha 

576 

577 # Check new value of a_j 

578 

579 phi_aj = phi(a_j) 

580 if (phi_aj > phi0 + c1*a_j*derphi0) or (phi_aj >= phi_lo): 

581 phi_rec = phi_hi 

582 a_rec = a_hi 

583 a_hi = a_j 

584 phi_hi = phi_aj 

585 else: 

586 derphi_aj = derphi(a_j) 

587 if abs(derphi_aj) <= -c2*derphi0 and extra_condition(a_j, phi_aj): 

588 a_star = a_j 

589 val_star = phi_aj 

590 valprime_star = derphi_aj 

591 break 

592 if derphi_aj*(a_hi - a_lo) >= 0: 

593 phi_rec = phi_hi 

594 a_rec = a_hi 

595 a_hi = a_lo 

596 phi_hi = phi_lo 

597 else: 

598 phi_rec = phi_lo 

599 a_rec = a_lo 

600 a_lo = a_j 

601 phi_lo = phi_aj 

602 derphi_lo = derphi_aj 

603 i += 1 

604 if (i > maxiter): 

605 # Failed to find a conforming step size 

606 a_star = None 

607 val_star = None 

608 valprime_star = None 

609 break 

610 return a_star, val_star, valprime_star 

611 

612 

613#------------------------------------------------------------------------------ 

614# Armijo line and scalar searches 

615#------------------------------------------------------------------------------ 

616 

617def line_search_armijo(f, xk, pk, gfk, old_fval, args=(), c1=1e-4, alpha0=1): 

618 """Minimize over alpha, the function ``f(xk+alpha pk)``. 

619 

620 Parameters 

621 ---------- 

622 f : callable 

623 Function to be minimized. 

624 xk : array_like 

625 Current point. 

626 pk : array_like 

627 Search direction. 

628 gfk : array_like 

629 Gradient of `f` at point `xk`. 

630 old_fval : float 

631 Value of `f` at point `xk`. 

632 args : tuple, optional 

633 Optional arguments. 

634 c1 : float, optional 

635 Value to control stopping criterion. 

636 alpha0 : scalar, optional 

637 Value of `alpha` at start of the optimization. 

638 

639 Returns 

640 ------- 

641 alpha 

642 f_count 

643 f_val_at_alpha 

644 

645 Notes 

646 ----- 

647 Uses the interpolation algorithm (Armijo backtracking) as suggested by 

648 Wright and Nocedal in 'Numerical Optimization', 1999, pp. 56-57 

649 

650 """ 

651 xk = np.atleast_1d(xk) 

652 fc = [0] 

653 

654 def phi(alpha1): 

655 fc[0] += 1 

656 return f(xk + alpha1*pk, *args) 

657 

658 if old_fval is None: 

659 phi0 = phi(0.) 

660 else: 

661 phi0 = old_fval # compute f(xk) -- done in past loop 

662 

663 derphi0 = np.dot(gfk, pk) 

664 alpha, phi1 = scalar_search_armijo(phi, phi0, derphi0, c1=c1, 

665 alpha0=alpha0) 

666 return alpha, fc[0], phi1 

667 

668 

669def line_search_BFGS(f, xk, pk, gfk, old_fval, args=(), c1=1e-4, alpha0=1): 

670 """ 

671 Compatibility wrapper for `line_search_armijo` 

672 """ 

673 r = line_search_armijo(f, xk, pk, gfk, old_fval, args=args, c1=c1, 

674 alpha0=alpha0) 

675 return r[0], r[1], 0, r[2] 

676 

677 

678def scalar_search_armijo(phi, phi0, derphi0, c1=1e-4, alpha0=1, amin=0): 

679 """Minimize over alpha, the function ``phi(alpha)``. 

680 

681 Uses the interpolation algorithm (Armijo backtracking) as suggested by 

682 Wright and Nocedal in 'Numerical Optimization', 1999, pp. 56-57 

683 

684 alpha > 0 is assumed to be a descent direction. 

685 

686 Returns 

687 ------- 

688 alpha 

689 phi1 

690 

691 """ 

692 phi_a0 = phi(alpha0) 

693 if phi_a0 <= phi0 + c1*alpha0*derphi0: 

694 return alpha0, phi_a0 

695 

696 # Otherwise, compute the minimizer of a quadratic interpolant: 

697 

698 alpha1 = -(derphi0) * alpha0**2 / 2.0 / (phi_a0 - phi0 - derphi0 * alpha0) 

699 phi_a1 = phi(alpha1) 

700 

701 if (phi_a1 <= phi0 + c1*alpha1*derphi0): 

702 return alpha1, phi_a1 

703 

704 # Otherwise, loop with cubic interpolation until we find an alpha which 

705 # satisfies the first Wolfe condition (since we are backtracking, we will 

706 # assume that the value of alpha is not too small and satisfies the second 

707 # condition. 

708 

709 while alpha1 > amin: # we are assuming alpha>0 is a descent direction 

710 factor = alpha0**2 * alpha1**2 * (alpha1-alpha0) 

711 a = alpha0**2 * (phi_a1 - phi0 - derphi0*alpha1) - \ 

712 alpha1**2 * (phi_a0 - phi0 - derphi0*alpha0) 

713 a = a / factor 

714 b = -alpha0**3 * (phi_a1 - phi0 - derphi0*alpha1) + \ 

715 alpha1**3 * (phi_a0 - phi0 - derphi0*alpha0) 

716 b = b / factor 

717 

718 alpha2 = (-b + np.sqrt(abs(b**2 - 3 * a * derphi0))) / (3.0*a) 

719 phi_a2 = phi(alpha2) 

720 

721 if (phi_a2 <= phi0 + c1*alpha2*derphi0): 

722 return alpha2, phi_a2 

723 

724 if (alpha1 - alpha2) > alpha1 / 2.0 or (1 - alpha2/alpha1) < 0.96: 

725 alpha2 = alpha1 / 2.0 

726 

727 alpha0 = alpha1 

728 alpha1 = alpha2 

729 phi_a0 = phi_a1 

730 phi_a1 = phi_a2 

731 

732 # Failed to find a suitable step length 

733 return None, phi_a1 

734 

735 

736#------------------------------------------------------------------------------ 

737# Non-monotone line search for DF-SANE 

738#------------------------------------------------------------------------------ 

739 

740def _nonmonotone_line_search_cruz(f, x_k, d, prev_fs, eta, 

741 gamma=1e-4, tau_min=0.1, tau_max=0.5): 

742 """ 

743 Nonmonotone backtracking line search as described in [1]_ 

744 

745 Parameters 

746 ---------- 

747 f : callable 

748 Function returning a tuple ``(f, F)`` where ``f`` is the value 

749 of a merit function and ``F`` the residual. 

750 x_k : ndarray 

751 Initial position. 

752 d : ndarray 

753 Search direction. 

754 prev_fs : float 

755 List of previous merit function values. Should have ``len(prev_fs) <= M`` 

756 where ``M`` is the nonmonotonicity window parameter. 

757 eta : float 

758 Allowed merit function increase, see [1]_ 

759 gamma, tau_min, tau_max : float, optional 

760 Search parameters, see [1]_ 

761 

762 Returns 

763 ------- 

764 alpha : float 

765 Step length 

766 xp : ndarray 

767 Next position 

768 fp : float 

769 Merit function value at next position 

770 Fp : ndarray 

771 Residual at next position 

772 

773 References 

774 ---------- 

775 [1] "Spectral residual method without gradient information for solving 

776 large-scale nonlinear systems of equations." W. La Cruz, 

777 J.M. Martinez, M. Raydan. Math. Comp. **75**, 1429 (2006). 

778 

779 """ 

780 f_k = prev_fs[-1] 

781 f_bar = max(prev_fs) 

782 

783 alpha_p = 1 

784 alpha_m = 1 

785 alpha = 1 

786 

787 while True: 

788 xp = x_k + alpha_p * d 

789 fp, Fp = f(xp) 

790 

791 if fp <= f_bar + eta - gamma * alpha_p**2 * f_k: 

792 alpha = alpha_p 

793 break 

794 

795 alpha_tp = alpha_p**2 * f_k / (fp + (2*alpha_p - 1)*f_k) 

796 

797 xp = x_k - alpha_m * d 

798 fp, Fp = f(xp) 

799 

800 if fp <= f_bar + eta - gamma * alpha_m**2 * f_k: 

801 alpha = -alpha_m 

802 break 

803 

804 alpha_tm = alpha_m**2 * f_k / (fp + (2*alpha_m - 1)*f_k) 

805 

806 alpha_p = np.clip(alpha_tp, tau_min * alpha_p, tau_max * alpha_p) 

807 alpha_m = np.clip(alpha_tm, tau_min * alpha_m, tau_max * alpha_m) 

808 

809 return alpha, xp, fp, Fp 

810 

811 

812def _nonmonotone_line_search_cheng(f, x_k, d, f_k, C, Q, eta, 

813 gamma=1e-4, tau_min=0.1, tau_max=0.5, 

814 nu=0.85): 

815 """ 

816 Nonmonotone line search from [1] 

817 

818 Parameters 

819 ---------- 

820 f : callable 

821 Function returning a tuple ``(f, F)`` where ``f`` is the value 

822 of a merit function and ``F`` the residual. 

823 x_k : ndarray 

824 Initial position. 

825 d : ndarray 

826 Search direction. 

827 f_k : float 

828 Initial merit function value. 

829 C, Q : float 

830 Control parameters. On the first iteration, give values 

831 Q=1.0, C=f_k 

832 eta : float 

833 Allowed merit function increase, see [1]_ 

834 nu, gamma, tau_min, tau_max : float, optional 

835 Search parameters, see [1]_ 

836 

837 Returns 

838 ------- 

839 alpha : float 

840 Step length 

841 xp : ndarray 

842 Next position 

843 fp : float 

844 Merit function value at next position 

845 Fp : ndarray 

846 Residual at next position 

847 C : float 

848 New value for the control parameter C 

849 Q : float 

850 New value for the control parameter Q 

851 

852 References 

853 ---------- 

854 .. [1] W. Cheng & D.-H. Li, ''A derivative-free nonmonotone line 

855 search and its application to the spectral residual 

856 method'', IMA J. Numer. Anal. 29, 814 (2009). 

857 

858 """ 

859 alpha_p = 1 

860 alpha_m = 1 

861 alpha = 1 

862 

863 while True: 

864 xp = x_k + alpha_p * d 

865 fp, Fp = f(xp) 

866 

867 if fp <= C + eta - gamma * alpha_p**2 * f_k: 

868 alpha = alpha_p 

869 break 

870 

871 alpha_tp = alpha_p**2 * f_k / (fp + (2*alpha_p - 1)*f_k) 

872 

873 xp = x_k - alpha_m * d 

874 fp, Fp = f(xp) 

875 

876 if fp <= C + eta - gamma * alpha_m**2 * f_k: 

877 alpha = -alpha_m 

878 break 

879 

880 alpha_tm = alpha_m**2 * f_k / (fp + (2*alpha_m - 1)*f_k) 

881 

882 alpha_p = np.clip(alpha_tp, tau_min * alpha_p, tau_max * alpha_p) 

883 alpha_m = np.clip(alpha_tm, tau_min * alpha_m, tau_max * alpha_m) 

884 

885 # Update C and Q 

886 Q_next = nu * Q + 1 

887 C = (nu * Q * (C + eta) + fp) / Q_next 

888 Q = Q_next 

889 

890 return alpha, xp, fp, Fp, C, Q