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