use crate::common::*;
use vecfx::*;
#[derive(Clone, Debug)]
pub struct CachedProblem<E>
where
E: FnMut(&[f64], &mut [f64]) -> f64,
{
f: E,
x: Vec<f64>,
fx: Option<f64>,
gx: Option<Vec<f64>>,
epsilon: f64,
neval: usize,
x_prev: Vec<f64>,
fx_prev: Option<f64>,
gx_prev: Option<Vec<f64>>,
}
impl<E> CachedProblem<E>
where
E: FnMut(&[f64], &mut [f64]) -> f64,
{
pub fn new(x: &[f64], f: E) -> Self {
CachedProblem {
f,
epsilon: 1e-8,
x: x.to_vec(),
fx: None,
gx: None,
neval: 0,
x_prev: x.to_vec(),
fx_prev: None,
gx_prev: None,
}
}
pub fn ncalls(&self) -> usize {
self.neval
}
fn eval(&mut self) -> (f64, &[f64]) {
let n = self.x.len();
let gx: &mut [f64] = self.gx.get_or_insert(vec![0.0; n]);
let v = (self.f)(&self.x, gx);
self.fx = Some(v);
self.neval += 1;
self.fx_prev = self.fx;
self.x_prev = self.x.clone();
self.gx_prev = Some(gx.to_vec());
(v, gx)
}
pub fn value(&mut self) -> f64 {
match self.fx {
Some(v) => v,
None => {
let (fx, _) = self.eval();
fx
}
}
}
pub fn value_prev(&self) -> f64 {
match self.fx_prev {
Some(fx) => fx,
None => panic!("not evaluated yet"),
}
}
pub fn gradient_prev(&self) -> &[f64] {
match self.gx_prev {
Some(ref gx) => gx,
None => panic!("not evaluated yet"),
}
}
pub fn gradient(&mut self) -> &[f64] {
match self.gx {
Some(ref gx) => gx,
None => {
let (_, gx) = self.eval();
gx
}
}
}
pub fn position(&self) -> &[f64] {
&self.x
}
pub fn take_line_step(&mut self, displ: &[f64], step: f64) {
if step * displ.vec2norm() > self.epsilon {
self.x.vecadd(displ, step);
self.fx = None;
self.gx = None;
}
}
pub fn revert(&mut self) {
self.fx = self.fx_prev;
self.x.veccpy(&self.x_prev);
self.gx = self.gx_prev.clone();
}
}