gosh_lbfgs/line_new.rs
1// docs
2// Copyright (c) 1990, Jorge Nocedal
3// Copyright (c) 2007-2010 Naoaki Okazaki
4// Copyright (c) 2018-2019 Wenping Guo
5// All rights reserved.
6
7//! # Find a satisfactory step length along predefined search direction
8//!
9//! # Example
10//! ```
11//! use liblbfgs::math::LbfgsMath;
12//! use liblbfgs::Problem;
13//! use liblbfgs::default_evaluate;
14//! use liblbfgs::line::LineSearch;
15//!
16//! const N: usize = 100;
17//! let mut x = [0.0; N];
18//! for i in (0..N).step_by(2) {
19//! x[i] = -1.2;
20//! x[i + 1] = 1.0;
21//! }
22//!
23//! // construct problem
24//! let mut prb = Problem::new(&mut x, default_evaluate(), None);
25//! prb.evaluate();
26//! // construct initial search direction
27//! prb.update_search_direction();
28//! // Compute the initial step
29//! let mut step = 1.0/prb.search_direction().vec2norm();
30//!
31//! let ls = LineSearch::default();
32//! let ncall = ls.find(&mut prb, &mut step).expect("line search");
33//! ```
34
35// [[file:../lbfgs.note::*imports][imports:1]]
36use crate::core::*;
37use crate::base::Problem;
38// imports:1 ends here
39
40// [[file:../lbfgs.note::*algorithm][algorithm:1]]
41/// Line search algorithms.
42#[derive(Debug, Copy, Clone, PartialEq)]
43pub enum LineSearchAlgorithm {
44 /// MoreThuente method proposd by More and Thuente. This is the default for
45 /// regular LBFGS.
46 MoreThuente,
47
48 ///
49 /// Backtracking method with the Armijo condition.
50 ///
51 /// The backtracking method finds the step length such that it satisfies
52 /// the sufficient decrease (Armijo) condition,
53 /// - f(x + a * d) <= f(x) + ftol * a * g(x)^T d,
54 ///
55 /// where x is the current point, d is the current search direction, and
56 /// a is the step length.
57 ///
58 BacktrackingArmijo,
59
60 /// Backtracking method with strong Wolfe condition.
61 ///
62 /// The backtracking method finds the step length such that it satisfies
63 /// both the Armijo condition (BacktrackingArmijo)
64 /// and the following condition,
65 /// - |g(x + a * d)^T d| <= gtol * |g(x)^T d|,
66 ///
67 /// where x is the current point, d is the current search direction, and
68 /// a is the step length.
69 ///
70 BacktrackingStrongWolfe,
71
72 ///
73 /// Backtracking method with regular Wolfe condition.
74 ///
75 /// The backtracking method finds the step length such that it satisfies
76 /// both the Armijo condition (BacktrackingArmijo)
77 /// and the curvature condition,
78 /// - g(x + a * d)^T d >= gtol * g(x)^T d,
79 ///
80 /// where x is the current point, d is the current search direction, and a
81 /// is the step length.
82 ///
83 BacktrackingWolfe,
84}
85
86impl Default for LineSearchAlgorithm {
87 /// The default algorithm (MoreThuente method).
88 fn default() -> Self {
89 LineSearchAlgorithm::MoreThuente
90 }
91}
92// algorithm:1 ends here
93
94// [[file:../lbfgs.note::*paramters][paramters:1]]
95#[derive(Debug, Copy, Clone)]
96pub struct LineSearch {
97 /// Various line search algorithms.
98 pub algorithm: LineSearchAlgorithm,
99
100 /// A parameter to control the accuracy of the line search routine.
101 ///
102 /// The default value is 1e-4. This parameter should be greater
103 /// than zero and smaller than 0.5.
104 pub ftol: f64,
105
106 /// A parameter to control the accuracy of the line search routine.
107 ///
108 /// The default value is 0.9. If the function and gradient evaluations are
109 /// inexpensive with respect to the cost of the iteration (which is
110 /// sometimes the case when solving very large problems) it may be
111 /// advantageous to set this parameter to a small value. A typical small
112 /// value is 0.1. This parameter shuold be greater than the ftol parameter
113 /// (1e-4) and smaller than 1.0.
114 pub gtol: f64,
115
116 /// xtol is a nonnegative input variable. termination occurs when the
117 /// relative width of the interval of uncertainty is at most xtol.
118 ///
119 /// The machine precision for floating-point values.
120 ///
121 /// This parameter must be a positive value set by a client program to
122 /// estimate the machine precision. The line search routine will terminate
123 /// with the status code (::LBFGSERR_ROUNDING_ERROR) if the relative width
124 /// of the interval of uncertainty is less than this parameter.
125 pub xtol: f64,
126
127 /// The minimum step of the line search routine.
128 ///
129 /// The default value is \c 1e-20. This value need not be modified unless
130 /// the exponents are too large for the machine being used, or unless the
131 /// problem is extremely badly scaled (in which case the exponents should be
132 /// increased).
133 pub min_step: f64,
134
135 /// The maximum step of the line search.
136 ///
137 /// The default value is \c 1e+20. This value need not be modified unless
138 /// the exponents are too large for the machine being used, or unless the
139 /// problem is extremely badly scaled (in which case the exponents should
140 /// be increased).
141 pub max_step: f64,
142
143 /// The maximum number of trials for the line search.
144 ///
145 /// This parameter controls the number of function and gradients evaluations
146 /// per iteration for the line search routine. The default value is 40. Set
147 /// this value to 0, will completely disable line search.
148 ///
149 pub max_linesearch: usize,
150
151 /// Make line search conditions use only gradients.
152 pub gradient_only: bool,
153}
154
155// TODO: better defaults
156impl Default for LineSearch {
157 fn default() -> Self {
158 LineSearch {
159 ftol: 1e-4,
160 gtol: 0.9,
161 xtol: 1e-16,
162 min_step: 1e-20,
163 max_step: 1e20,
164 max_linesearch: 40,
165 gradient_only: false,
166 algorithm: LineSearchAlgorithm::default(),
167 }
168 }
169}
170// paramters:1 ends here
171
172// [[file:../lbfgs.note::*adhoc][adhoc:1]]
173impl LineSearch {
174 fn validate_step(&self, step: f64) -> Result<()> {
175 // The step is the minimum value.
176 if step < self.min_step {
177 bail!("The line-search step became smaller than LineSearch::min_step.");
178 }
179 // The step is the maximum value.
180 if step > self.max_step {
181 bail!("The line-search step became larger than LineSearch::max_step.");
182 }
183
184 Ok(())
185 }
186}
187// adhoc:1 ends here
188
189// [[file:../lbfgs.note::*entry][entry:1]]
190impl LineSearch {
191 /// Unified entry for line searches
192 ///
193 /// # Arguments
194 ///
195 /// * step: initial step size. On output it will be the optimal step size.
196 /// * direction: proposed searching direction
197 /// * eval_fn: a callback function to evaluate `Problem`
198 ///
199 /// # Return
200 ///
201 /// * On success, return the number of line searching iterations
202 ///
203 pub(crate) fn find<'a, E>(&self, prb: &mut Problem<'a, E>, step: &mut f64) -> Result<usize> {
204 // Check the input parameters for errors.
205 ensure!(
206 step.is_sign_positive(),
207 "A logic error (negative line-search step) occurred: {}",
208 step
209 );
210
211 // Search for an optimal step.
212 let ls = if self.algorithm == MoreThuente && !prb.orthantwise() {
213 if !self.gradient_only {
214 line_search_morethuente(prb, step, &self)
215 } else {
216 bail!("Gradient only optimization is incompatible with MoreThuente line search.");
217 }
218 } else {
219 line_search_backtracking(prb, step, &self)
220 }
221 .unwrap_or_else(|err| {
222 // Revert to the previous point.
223 error!("line search failed, revert to the previous point!");
224 prb.revert();
225 println!("{:?}", err);
226
227 0
228 });
229
230 Ok(ls)
231 }
232}
233// entry:1 ends here
234
235// [[file:../lbfgs.note::*Original documentation by J. Nocera (lbfgs.f)][Original documentation by J. Nocera (lbfgs.f):1]]
236
237// Original documentation by J. Nocera (lbfgs.f):1 ends here
238
239// [[file:../lbfgs.note::*core][core:1]]
240use crate::math::*;
241
242pub(crate) fn line_search_morethuente<'a, E>(
243 prb: &mut Problem<'a, E>,
244 stp: &mut f64, // Step size
245 param: &LineSearch, // line search parameters
246) -> Result<usize> {
247 // Initialize local variables.
248 let dginit = prb.dginit()?;
249 let mut brackt = false;
250 let mut stage1 = 1;
251 let mut uinfo = 0;
252
253 let finit = prb.fx;
254 let dgtest = param.ftol * dginit;
255 let mut width = param.max_step - param.min_step;
256 let mut prev_width = 2.0 * width;
257
258 // The variables stx, fx, dgx contain the values of the step,
259 // function, and directional derivative at the best step.
260 // The variables sty, fy, dgy contain the value of the step,
261 // function, and derivative at the other endpoint of
262 // the interval of uncertainty.
263 // The variables stp, f, dg contain the values of the step,
264 // function, and derivative at the current step.
265 let (mut stx, mut sty) = (0.0, 0.0);
266 let mut fx = finit;
267 let mut fy = finit;
268 let mut dgy = dginit;
269 let mut dgx = dgy;
270
271 for count in 0..param.max_linesearch {
272 // Set the minimum and maximum steps to correspond to the
273 // present interval of uncertainty.
274 let (stmin, stmax) = if brackt {
275 (if stx <= sty { stx } else { sty }, if stx >= sty { stx } else { sty })
276 } else {
277 (stx, *stp + 4.0 * (*stp - stx))
278 };
279
280 // Clip the step in the range of [stpmin, stpmax].
281 if *stp < param.min_step {
282 *stp = param.min_step
283 }
284 if param.max_step < *stp {
285 *stp = param.max_step
286 }
287
288 // If an unusual termination is to occur then let
289 // stp be the lowest point obtained so far.
290 if brackt && (*stp <= stmin || stmax <= *stp || param.max_linesearch <= count + 1 || uinfo != 0)
291 || brackt && stmax - stmin <= param.xtol * stmax
292 {
293 *stp = stx
294 }
295
296 prb.take_line_step(*stp);
297
298 // Evaluate the function and gradient values.
299 prb.evaluate()?;
300 let f = prb.fx;
301 let dg = prb.dg_unchecked();
302 let ftest1 = finit + *stp * dgtest;
303
304 // Test for errors and convergence.
305 if brackt && (*stp <= stmin || stmax <= *stp || uinfo != 0i32) {
306 // Rounding errors prevent further progress.
307 bail!(
308 "A rounding error occurred; alternatively, no line-search step
309satisfies the sufficient decrease and curvature conditions."
310 );
311 }
312
313 if brackt && stmax - stmin <= param.xtol * stmax {
314 bail!("Relative width of the interval of uncertainty is at most xtol.");
315 }
316
317 // FIXME: float == float?
318 if *stp == param.max_step && f <= ftest1 && dg <= dgtest {
319 // The step is the maximum value.
320 bail!("The line-search step became larger than LineSearch::max_step.");
321 }
322 // FIXME: float == float?
323 if *stp == param.min_step && (ftest1 < f || dgtest <= dg) {
324 // The step is the minimum value.
325 bail!("The line-search step became smaller than LineSearch::min_step.");
326 }
327
328 if dg.abs() <= param.gtol * -dginit {
329 // the directional derivative condition hold.
330 return Ok(count);
331 } else if f <= ftest1 && dg.abs() <= param.gtol * -dginit {
332 // The sufficient decrease condition and the directional derivative condition hold.
333 return Ok(count);
334 } else {
335 // In the first stage we seek a step for which the modified
336 // function has a nonpositive value and nonnegative derivative.
337 if 0 != stage1 && f <= ftest1 && param.ftol.min(param.gtol) * dginit <= dg {
338 stage1 = 0;
339 }
340
341 // A modified function is used to predict the step only if
342 // we have not obtained a step for which the modified
343 // function has a nonpositive function value and nonnegative
344 // derivative, and if a lower function value has been
345 // obtained but the decrease is not sufficient.
346 if 0 != stage1 && ftest1 < f && f <= fx {
347 // Define the modified function and derivative values.
348 let fm = f - *stp * dgtest;
349 let mut fxm = fx - stx * dgtest;
350 let mut fym = fy - sty * dgtest;
351 let dgm = dg - dgtest;
352 let mut dgxm = dgx - dgtest;
353 let mut dgym = dgy - dgtest;
354
355 // Call update_trial_interval() to update the interval of
356 // uncertainty and to compute the new step.
357 uinfo = mcstep::update_trial_interval(
358 &mut stx,
359 &mut fxm,
360 &mut dgxm,
361 &mut sty,
362 &mut fym,
363 &mut dgym,
364 &mut *stp,
365 fm,
366 dgm,
367 stmin,
368 stmax,
369 &mut brackt,
370 )?;
371
372 // Reset the function and gradient values for f.
373 fx = fxm + stx * dgtest;
374 fy = fym + sty * dgtest;
375 dgx = dgxm + dgtest;
376 dgy = dgym + dgtest
377 } else {
378 uinfo = mcstep::update_trial_interval(
379 &mut stx,
380 &mut fx,
381 &mut dgx,
382 &mut sty,
383 &mut fy,
384 &mut dgy,
385 &mut *stp,
386 f,
387 dg,
388 stmin,
389 stmax,
390 &mut brackt,
391 )?;
392 }
393
394 // Force a sufficient decrease in the interval of uncertainty.
395 if !(brackt) {
396 continue;
397 }
398
399 if 0.66 * prev_width <= (sty - stx).abs() {
400 *stp = stx + 0.5 * (sty - stx)
401 }
402
403 prev_width = width;
404 width = (sty - stx).abs()
405 }
406 }
407
408 // Maximum number of iteration.
409 info!("The line-search routine reaches the maximum number of evaluations.");
410
411 Ok(param.max_linesearch)
412}
413// core:1 ends here
414
415// [[file:../lbfgs.note::*core][core:1]]
416/// Represents the original MCSTEP subroutine by J. Nocera, which is a variant
417/// of More' and Thuente's routine.
418///
419/// The purpose of mcstep is to compute a safeguarded step for a linesearch and
420/// to update an interval of uncertainty for a minimizer of the function.
421///
422/// Documentation is adopted from the original Fortran codes.
423mod mcstep {
424 // dependencies
425 use super::{cubic_minimizer, cubic_minimizer2, quard_minimizer, quard_minimizer2};
426
427 use crate::core::*;
428
429 ///
430 /// Update a safeguarded trial value and interval for line search.
431 ///
432 /// This function assumes that the derivative at the point of x in the
433 /// direction of the step. If the bracket is set to true, the minimizer has
434 /// been bracketed in an interval of uncertainty with endpoints between x
435 /// and y.
436 ///
437 /// # Arguments
438 ///
439 /// * x, fx, and dx: variables which specify the step, the function, and the
440 /// derivative at the best step obtained so far. The derivative must be
441 /// negative in the direction of the step, that is, dx and t-x must have
442 /// opposite signs. On output these parameters are updated appropriately.
443 ///
444 /// * y, fy, and dy: variables which specify the step, the function, and
445 /// the derivative at the other endpoint of the interval of uncertainty. On
446 /// output these parameters are updated appropriately.
447 ///
448 /// * t, ft, and dt: variables which specify the step, the function, and the
449 /// derivative at the current step. If bracket is set true then on input t
450 /// must be between x and y. On output t is set to the new step.
451 ///
452 /// * tmin, tmax: lower and upper bounds for the step.
453 ///
454 /// * `brackt`: Specifies if a minimizer has been bracketed. If the
455 /// minimizer has not been bracketed then on input brackt must be set false.
456 /// If the minimizer is bracketed then on output `brackt` is set true.
457 ///
458 /// # Return
459 /// - Status value. Zero indicates a normal termination.
460 ///
461 pub(crate) fn update_trial_interval(
462 x: &mut f64,
463 fx: &mut f64,
464 dx: &mut f64,
465 y: &mut f64,
466 fy: &mut f64,
467 dy: &mut f64,
468 t: &mut f64,
469 ft: f64,
470 dt: f64,
471 tmin: f64,
472 tmax: f64,
473 brackt: &mut bool,
474 ) -> Result<i32> {
475 // fsigndiff
476 let dsign = dt * (*dx / (*dx).abs()) < 0.0;
477 // minimizer of an interpolated cubic.
478 let mut mc = 0.;
479 // minimizer of an interpolated quadratic.
480 let mut mq = 0.;
481 // new trial value.
482 let mut newt = 0.;
483
484 // Check the input parameters for errors.
485 if *brackt {
486 if *t <= x.min(*y) || x.max(*y) <= *t {
487 // The trival value t is out of the interval.
488 bail!("The line-search step went out of the interval of uncertainty.");
489 } else if 0.0 <= *dx * (*t - *x) {
490 // The function must decrease from x.
491 bail!("The current search direction increases the objective function value.");
492 } else if tmax < tmin {
493 // Incorrect tmin and tmax specified.
494 bail!("A logic error occurred; alternatively, the interval of uncertainty became too small.");
495 }
496 }
497
498 // Trial value selection.
499 let bound = if *fx < ft {
500 // Case 1: a higher function value.
501 // The minimum is brackt. If the cubic minimizer is closer
502 // to x than the quadratic one, the cubic one is taken, else
503 // the average of the minimizers is taken.
504 *brackt = true;
505 cubic_minimizer(&mut mc, *x, *fx, *dx, *t, ft, dt);
506 quard_minimizer(&mut mq, *x, *fx, *dx, *t, ft);
507 if (mc - *x).abs() < (mq - *x).abs() {
508 newt = mc
509 } else {
510 newt = mc + 0.5 * (mq - mc)
511 }
512
513 1
514 } else if dsign {
515 // Case 2: a lower function value and derivatives of
516 // opposite sign. The minimum is brackt. If the cubic
517 // minimizer is closer to x than the quadratic (secant) one,
518 // the cubic one is taken, else the quadratic one is taken.
519 *brackt = true;
520 cubic_minimizer(&mut mc, *x, *fx, *dx, *t, ft, dt);
521 quard_minimizer2(&mut mq, *x, *dx, *t, dt);
522 if (mc - *t).abs() > (mq - *t).abs() {
523 newt = mc
524 } else {
525 newt = mq
526 }
527
528 0
529 } else if dt.abs() < (*dx).abs() {
530 // Case 3: a lower function value, derivatives of the
531 // same sign, and the magnitude of the derivative decreases.
532 // The cubic minimizer is only used if the cubic tends to
533 // infinity in the direction of the minimizer or if the minimum
534 // of the cubic is beyond t. Otherwise the cubic minimizer is
535 // defined to be either tmin or tmax. The quadratic (secant)
536 // minimizer is also computed and if the minimum is brackt
537 // then the the minimizer closest to x is taken, else the one
538 // farthest away is taken.
539 cubic_minimizer2(&mut mc, *x, *fx, *dx, *t, ft, dt, tmin, tmax);
540 quard_minimizer2(&mut mq, *x, *dx, *t, dt);
541 if *brackt {
542 if (*t - mc).abs() < (*t - mq).abs() {
543 newt = mc
544 } else {
545 newt = mq
546 }
547 } else if (*t - mc).abs() > (*t - mq).abs() {
548 newt = mc
549 } else {
550 newt = mq
551 }
552
553 1
554 } else {
555 // Case 4: a lower function value, derivatives of the
556 // same sign, and the magnitude of the derivative does
557 // not decrease. If the minimum is not brackt, the step
558 // is either tmin or tmax, else the cubic minimizer is taken.
559 if *brackt {
560 cubic_minimizer(&mut newt, *t, ft, dt, *y, *fy, *dy);
561 } else if *x < *t {
562 newt = tmax
563 } else {
564 newt = tmin
565 }
566
567 0
568 };
569
570 // Update the interval of uncertainty. This update does not
571 // depend on the new step or the case analysis above.
572 // - Case a: if f(x) < f(t),
573 // x <- x, y <- t.
574 // - Case b: if f(t) <= f(x) && f'(t)*f'(x) > 0,
575 // x <- t, y <- y.
576 // - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0,
577 // x <- t, y <- x.
578 if *fx < ft {
579 /* Case a */
580 *y = *t;
581 *fy = ft;
582 *dy = dt
583 } else {
584 /* Case c */
585 if dsign {
586 *y = *x;
587 *fy = *fx;
588 *dy = *dx
589 }
590 /* Cases b and c */
591 *x = *t;
592 *fx = ft;
593 *dx = dt
594 }
595
596 // Clip the new trial value in [tmin, tmax].
597 if tmax < newt {
598 newt = tmax
599 }
600 if newt < tmin {
601 newt = tmin
602 }
603
604 // Redefine the new trial value if it is close to the upper bound of the
605 // interval.
606 if *brackt && 0 != bound {
607 mq = *x + 0.66 * (*y - *x);
608 if *x < *y {
609 if mq < newt {
610 newt = mq
611 }
612 } else if newt < mq {
613 newt = mq
614 }
615 }
616
617 // Return the new trial value.
618 *t = newt;
619
620 Ok(0)
621 }
622}
623// core:1 ends here
624
625// [[file:../lbfgs.note::*interpolation][interpolation:1]]
626/// Find a minimizer of an interpolated cubic function.
627///
628/// # Arguments
629/// * `cm`: The minimizer of the interpolated cubic.
630/// * `u` : The value of one point, u.
631/// * `fu`: The value of f(u).
632/// * `du`: The value of f'(u).
633/// * `v` : The value of another point, v.
634/// * `fv`: The value of f(v).
635/// * `dv`: The value of f'(v).
636#[inline]
637fn cubic_minimizer(cm: &mut f64, u: f64, fu: f64, du: f64, v: f64, fv: f64, dv: f64) {
638 let d = v - u;
639 let theta = (fu - fv) * 3.0 / d + du + dv;
640
641 let mut p = theta.abs();
642 let mut q = du.abs();
643 let mut r = dv.abs();
644 let s = (p.max(q)).max(r); // max3(p, q, r)
645 let a = theta / s;
646 let mut gamma = s * (a * a - du / s * (dv / s)).sqrt();
647 if v < u {
648 gamma = -gamma
649 }
650 p = gamma - du + theta;
651 q = gamma - du + gamma + dv;
652 r = p / q;
653 *cm = u + r * d;
654}
655
656/// Find a minimizer of an interpolated cubic function.
657///
658/// # Arguments
659/// * cm : The minimizer of the interpolated cubic.
660/// * u : The value of one point, u.
661/// * fu : The value of f(u).
662/// * du : The value of f'(u).
663/// * v : The value of another point, v.
664/// * fv : The value of f(v).
665/// * dv : The value of f'(v).
666/// * xmin: The minimum value.
667/// * xmax: The maximum value.
668#[inline]
669fn cubic_minimizer2(cm: &mut f64, u: f64, fu: f64, du: f64, v: f64, fv: f64, dv: f64, xmin: f64, xmax: f64) {
670 // STP - STX
671 let d = v - u;
672 let theta = (fu - fv) * 3.0 / d + du + dv;
673 let mut p = theta.abs();
674 let mut q = du.abs();
675 let mut r = dv.abs();
676 // s = max3(p, q, r);
677 let s = (p.max(q)).max(r); // max3(p, q, r)
678 let a = theta / s;
679
680 let mut gamma = s * (0f64.max(a * a - du / s * (dv / s)).sqrt());
681 // STX < STP
682 if u < v {
683 gamma = -gamma
684 }
685 p = gamma - dv + theta;
686 q = gamma - dv + gamma + du;
687 r = p / q;
688 if r < 0.0 && gamma != 0.0 {
689 *cm = v - r * d;
690 // } else if a < 0 as f64 {
691 // ELSE IF (STP .GT. STX) THEN
692 } else if v > u {
693 *cm = xmax;
694 } else {
695 *cm = xmin;
696 }
697}
698
699/// Find a minimizer of an interpolated quadratic function.
700///
701/// # Arguments
702/// * qm : The minimizer of the interpolated quadratic.
703/// * u : The value of one point, u.
704/// * fu : The value of f(u).
705/// * du : The value of f'(u).
706/// * v : The value of another point, v.
707/// * fv : The value of f(v).
708#[inline]
709fn quard_minimizer(qm: &mut f64, u: f64, fu: f64, du: f64, v: f64, fv: f64) {
710 let a = v - u;
711 *qm = u + du / ((fu - fv) / a + du) / 2.0 * a;
712}
713
714/// Find a minimizer of an interpolated quadratic function.
715///
716/// # Arguments
717/// * `qm` : The minimizer of the interpolated quadratic.
718/// * `u` : The value of one point, u.
719/// * `du` : The value of f'(u).
720/// * `v` : The value of another point, v.
721/// * `dv` : The value of f'(v).
722#[inline]
723fn quard_minimizer2(qm: &mut f64, u: f64, du: f64, v: f64, dv: f64) {
724 let a = u - v;
725 *qm = v + dv / (dv - du) * a;
726}
727// interpolation:1 ends here
728
729// [[file:../lbfgs.note::*core][core:1]]
730use self::LineSearchAlgorithm::*;
731
732/// `prb` holds input variables `x`, gradient `gx` arrays, and function value
733/// `fx`. on input it must contain the base point for the line search. on output
734/// it contains data on x + stp*d.
735pub(crate) fn line_search_backtracking<'a, E>(
736 prb: &mut Problem<'a, E>,
737 stp: &mut f64, // step length
738 param: &LineSearch, // line search parameters
739) -> Result<usize> {
740 let dginit = prb.dginit()?;
741 let dec: f64 = 0.5;
742 let inc: f64 = 2.1;
743
744 // quick wrapper
745 let orthantwise = prb.orthantwise();
746
747 // The initial value of the objective function.
748 let finit = prb.fx;
749 let dgtest = param.ftol * dginit;
750
751 let mut width: f64;
752 for count in 0..param.max_linesearch {
753 prb.take_line_step(*stp);
754
755 // Evaluate the function and gradient values.
756 prb.evaluate()?;
757
758 if prb.fx > finit + *stp * dgtest {
759 width = dec;
760 } else if param.algorithm == BacktrackingArmijo || orthantwise {
761 // The sufficient decrease condition.
762 // Exit with the Armijo condition.
763 return Ok(count);
764 } else {
765 // Check the Wolfe condition.
766 let dg = prb.dg_unchecked();
767 if dg < param.gtol * dginit {
768 width = inc
769 } else if param.algorithm == BacktrackingWolfe {
770 // Exit with the regular Wolfe condition.
771 return Ok(count);
772 } else if dg > -param.gtol * dginit {
773 width = dec
774 } else {
775 return Ok(count);
776 }
777 }
778
779 // allow energy rises
780 // only check strong wolfe condition
781 if param.gradient_only {
782 info!("allow energy rises");
783 let dg = prb.dg_unchecked();
784 if dg.abs() <= -param.gtol * dginit.abs() {
785 return Ok(count);
786 }
787 }
788
789 param.validate_step(*stp)?;
790 *stp *= width
791 }
792
793 // Maximum number of iteration.
794 info!("The line-search routine reaches the maximum number of evaluations.");
795
796 Ok(param.max_linesearch)
797}
798// core:1 ends here