use super::algorithm::setulb;
use super::timer::timer;
use log::info;
pub const START: i32 = 1;
pub const NEW_X: i32 = 2;
pub const ABNORMAL: i32 = 3;
pub const FG: i32 = 10;
pub const FG_END: i32 = 15;
pub const CONV_GRAD: i32 = 21;
pub const CONV_F: i32 = 22;
pub const STOP: i32 = 30;
pub const STOP_END: i32 = 40;
pub const STOP_ITER: i32 = 32;
pub const STOP_GRAD: i32 = 33;
pub const ERROR: i32 = 200;
pub const ERROR_END: i32 = 240;
pub struct LbfgsbParameters {
pub m: usize,
pub pgtol: f64,
pub factr: f64,
pub time_limit: f64,
pub max_iter: i32,
}
impl Default for LbfgsbParameters {
fn default() -> Self {
LbfgsbParameters {
m: 10,
pgtol: 1e-5,
factr: 1e7,
time_limit: 0.2,
max_iter: 1000,
}
}
}
pub struct LbfgsbMinimizer<'a, F, G>
where
F: Fn(&Vec<f64>) -> f64,
G: Fn(&Vec<f64>) -> Vec<f64>,
{
n: usize,
x: &'a mut Vec<f64>,
f: &'a F,
g: &'a G,
l: Vec<f64>,
u: Vec<f64>,
nbd: Vec<i32>,
wa: Vec<f64>,
iwa: Vec<i32>,
task: i32,
csave: i32,
lsave: Vec<bool>,
isave: Vec<i32>,
dsave: Vec<f64>,
params: LbfgsbParameters
}
impl<'a, F, G> LbfgsbMinimizer<'a, F, G>
where
F: Fn(&Vec<f64>) -> f64,
G: Fn(&Vec<f64>) -> Vec<f64>,
{
pub fn new(
x0: &'a mut Vec<f64>,
f: &'a F,
g: &'a G,
params: Option<LbfgsbParameters>,
) -> Self {
let n = x0.len();
let params = params.unwrap_or_default();
let l = vec![f64::NEG_INFINITY; n];
let u = vec![f64::INFINITY; n];
LbfgsbMinimizer {
n: n,
x: x0,
l: vec![0.0f64;n as usize],
u: vec![0.0f64;n as usize],
nbd: vec![0;n as usize],
f: f,
g: g,
wa: vec![0.0f64;(2*params.m*n+11*params.m*params.m+params.m*n+8*params.m) as usize],
iwa: vec![0;3*n as usize],
task: 0,
csave: 0,
lsave: vec![false;4],
isave: vec![0;44],
dsave: vec![0.0f64;30],
params: params
}
}
pub fn minimize(&mut self) {
let mut f_val = (self.f)(self.x);
let mut g_val = (self.g)(self.x);
self.task = START;
let time_begin = timer();
loop {
setulb(self.n,
self.params.m,
self.x.as_mut_slice(),
self.l.as_mut_slice(),
self.u.as_mut_slice(),
self.nbd.as_mut_slice(),
&mut f_val,
g_val.as_mut_slice(),
self.params.factr,
self.params.pgtol,
self.wa.as_mut_slice(),
self.iwa.as_mut_slice(),
&mut self.task,
&mut self.csave,
self.lsave.as_mut_slice(),
self.isave.as_mut_slice(),
self.dsave.as_mut_slice()
);
if FG <= self.task && self.task <= FG_END {
f_val = (self.f)(self.x);
g_val = (self.g)(self.x);
}
else if self.task == NEW_X {
if self.isave[33] >= self.params.max_iter {
self.task = STOP_ITER;
}
if self.dsave[12] <= (f_val.abs() + 1.0) * 1e-10 {
self.task = STOP_GRAD;
}
info!("Iterate {} nfg = {} f = {} |proj g| = {}",
self.isave[29], self.isave[33], f_val, self.dsave[12]);
}
else {
break;
}
}
}
pub fn get_x(&self) -> Vec<f64> {
self.x.clone()
}
pub fn set_lower_bound(&mut self, index: usize, value: f64) {
if self.nbd[index] == 1 || self.nbd[index] == 2 {
println!("Variable already has Lower Bound");
} else {
let temp = self.nbd[index] - 1;
self.nbd[index] = if temp < 0 {
-temp
} else {
temp
};
self.l[index] = value;
}
}
pub fn set_upper_bound(&mut self, index: usize, value: f64) {
if self.nbd[index] == 3 || self.nbd[index] == 2 {
println!("Variable already has Lower Bound");
} else {
self.nbd[index] = 3 - self.nbd[index];
self.u[index] = value;
}
}
pub fn set_termination_tolerance(&mut self, t: f64) {
self.params.factr = t;
}
pub fn set_tolerance(&mut self, t: f64) {
self.params.pgtol = t;
}
pub fn max_iteration(&mut self, i: i32) {
self.params.max_iter = i;
}
pub fn set_metric_correction(&mut self, m: i32) {
self.params.m = m as usize;
}
}