use super::*;
pub struct BackTrackingB {
c1: Floating, beta: Floating, lower_bound: DVector<Floating>,
upper_bound: DVector<Floating>,
}
impl BackTrackingB {
pub fn new(
c1: Floating,
beta: Floating,
lower_bound: DVector<Floating>,
upper_bound: DVector<Floating>,
) -> Self {
BackTrackingB {
c1,
beta,
lower_bound,
upper_bound,
}
}
fn sufficient_decrease_with_bounds(
&self,
x0: &DVector<Floating>,
x: &DVector<Floating>,
f0: &Floating,
f: &Floating,
t: &Floating, ) -> bool {
let diff = x - x0;
f - f0 <= (-&self.c1 / t) * diff.dot(&diff)
}
}
impl HasBounds for BackTrackingB {
fn lower_bound(&self) -> &DVector<Floating> {
&self.lower_bound
}
fn upper_bound(&self) -> &DVector<Floating> {
&self.upper_bound
}
fn set_lower_bound(&mut self, lower_bound: DVector<Floating>) {
self.lower_bound = lower_bound;
}
fn set_upper_bound(&mut self, upper_bound: DVector<Floating>) {
self.upper_bound = upper_bound;
}
}
impl LineSearch for BackTrackingB {
fn compute_step_len(
&mut self,
x_k: &DVector<Floating>,
eval_x_k: &FuncEvalMultivariate,
direction_k: &DVector<Floating>,
oracle: & mut impl FnMut(&DVector<Floating>) -> FuncEvalMultivariate,
max_iter: usize,
) -> Floating {
let mut t = 1.0;
let mut i = 0;
while max_iter > i {
let x_kp1 = x_k + t * direction_k;
let x_kp1 = x_kp1.box_projection(&self.lower_bound, &self.upper_bound);
let eval_kp1 = oracle(&x_kp1);
if eval_kp1.f().is_nan() || eval_kp1.f().is_infinite() {
trace!(target: "backtracking_b line search", "Step size too big: next iterate is out of domain. Decreasing step by beta ({:?})", x_kp1);
t *= self.beta;
continue;
}
if self.sufficient_decrease_with_bounds(x_k, &x_kp1, eval_x_k.f(), eval_kp1.f(), &t) {
trace!(target: "backtracking_b line search", "Modified Armijo rule met. Exiting with step size: {:?} at iteration {:?}", t, i);
return t;
}
t *= self.beta;
i += 1;
}
trace!(target: "backtracking_b line search", "Max iter reached. Early stopping.");
t
}
}