Skip to main content

conspire/math/optimize/nonlinear/step_size/
mod.rs

1#[cfg(test)]
2mod test;
3
4mod barzilai_borwein;
5
6pub use barzilai_borwein::{BarzilaiBorwein, Step as BarzilaiBorweinStep};
7
8use crate::math::{Erase, Scalar, Tensor};
9use std::fmt::{Debug, Display};
10
11/// Ways of estimating a step size from the last two iterates.
12///
13/// What an estimator is given are the products of the change in solution
14/// $`s`$ and the change in residual $`y`$ between them, and the number of
15/// estimates it has made before, so that it holds nothing of its own.
16pub trait StepEstimator: Clone + Debug + Display {
17    /// The step size suggested by $`s^Ts`$, $`s^Ty`$ and $`y^Ty`$.
18    fn estimate(&self, ss: Scalar, sy: Scalar, yy: Scalar, iteration: usize) -> Scalar;
19}
20
21/// What a step size estimate is made from: the last solution and residual.
22///
23/// The change in each since they were last shown is what an estimator is
24/// given the products of. Where the change says nothing, the step size is
25/// left as it was.
26pub(crate) struct Secant<X, F> {
27    iteration: usize,
28    solution: X,
29    residual: Option<F>,
30}
31
32impl<X, F, E> Secant<X, F>
33where
34    X: Erase<Erased = E> + Tensor,
35    F: Erase<Erased = E> + Tensor,
36    E: Tensor,
37{
38    pub(crate) fn new(solution: &X) -> Self {
39        Self {
40            iteration: 0,
41            solution: solution.clone(),
42            residual: None,
43        }
44    }
45    pub(crate) fn solution(&self) -> &X {
46        &self.solution
47    }
48    pub(crate) fn step_size(
49        &mut self,
50        estimator: &impl StepEstimator,
51        solution: &X,
52        residual: &F,
53        step_size: Scalar,
54    ) -> Scalar {
55        self.solution -= solution;
56        let change = self.residual.get_or_insert_with(|| {
57            let mut zero = residual.clone();
58            zero *= 0.0;
59            zero
60        });
61        *change -= residual;
62        let trial = estimator.estimate(
63            self.solution
64                .erase()
65                .full_contraction(self.solution.erase()),
66            change.erase().full_contraction(self.solution.erase()),
67            change.erase().full_contraction(change.erase()),
68            self.iteration,
69        );
70        *change = residual.clone();
71        self.solution = solution.clone();
72        self.iteration += 1;
73        if trial.abs() > 0.0 && !trial.is_nan() {
74            trial.abs()
75        } else {
76            step_size
77        }
78    }
79}