use crate::*;
use bindings::{FG, FG_END, NEW_X, START};
use anyhow::Result;
fn is_fg(task: i64) -> bool {
let task = task as u32;
task >= FG && task <= FG_END
}
pub struct LbfgsbParameter {
pub m: usize,
pub factr: f64,
pub pgtol: f64,
pub iprint: i64,
}
impl Default for LbfgsbParameter {
fn default() -> Self {
Self {
m: 5,
factr: 1E7,
pgtol: 1E-5,
iprint: -1,
}
}
}
pub struct LbfgsbProblem<E>
where
E: FnMut(&[f64], &mut [f64]) -> Result<f64>,
{
x: Vec<f64>,
g: Vec<f64>,
f: f64,
l: Vec<f64>,
u: Vec<f64>,
nbd: Vec<i64>,
eval_fn: E,
}
impl<E> LbfgsbProblem<E>
where
E: FnMut(&[f64], &mut [f64]) -> Result<f64>,
{
pub fn build(x: Vec<f64>, eval_fn: E) -> Self {
let n = x.len();
Self {
x,
g: vec![0.0; n],
f: 0.0,
l: vec![0.0; n],
u: vec![0.0; n],
nbd: vec![0; n],
eval_fn,
}
}
pub fn set_bounds<B>(&mut self, bounds: B)
where
B: IntoIterator<Item = (Option<f64>, Option<f64>)>,
{
for (i, b) in bounds.into_iter().enumerate() {
match b {
(Some(l), Some(u)) => {
self.l[i] = l;
self.u[i] = u;
self.nbd[i] = 2;
}
(None, None) => {
self.nbd[i] = 0;
}
(Some(l), None) => {
self.l[i] = l;
self.nbd[i] = 1;
}
(None, Some(u)) => {
self.u[i] = u;
self.nbd[i] = 3;
}
}
}
}
}
pub struct LbfgsbState<E>
where
E: FnMut(&[f64], &mut [f64]) -> Result<f64>,
{
problem: LbfgsbProblem<E>,
param: LbfgsbParameter,
wa: Vec<f64>,
iwa: Vec<i64>,
csave: [i64; 60],
dsave: [f64; 29],
isave: [i64; 44],
lsave: [i64; 4],
task: i64,
}
impl<E> LbfgsbState<E>
where
E: FnMut(&[f64], &mut [f64]) -> Result<f64>,
{
pub fn new(problem: LbfgsbProblem<E>, param: LbfgsbParameter) -> Self {
let n = problem.x.len();
let m = param.m;
let wa = vec![0.0; 2 * m * n + 5 * n + 11 * m * m + 8 * m];
let iwa = vec![0; 3 * n];
Self {
csave: [0; 60],
dsave: [0.0; 29],
isave: [0; 44],
lsave: [0; 4],
task: START.into(),
problem,
param,
wa,
iwa,
}
}
pub fn minimize(&mut self) -> Result<()> {
let f = &mut self.problem.f;
let x = &mut self.problem.x;
let g = &mut self.problem.g;
let l = &self.problem.l;
let u = &self.problem.u;
let nbd = &self.problem.nbd;
let param = &self.param;
let n = x.len();
let m = param.m;
loop {
unsafe {
#[allow(clashing_extern_declarations)]
crate::setulb(
&(n as i64), &(m as i64), x.as_mut_ptr(), l.as_ptr(), u.as_ptr(), nbd.as_ptr(), f, g.as_mut_ptr(), ¶m.factr, ¶m.pgtol, self.wa.as_mut_ptr(), self.iwa.as_mut_ptr(), &mut self.task, ¶m.iprint, self.csave.as_mut_ptr(), self.lsave.as_mut_ptr(), self.isave.as_mut_ptr(), self.dsave.as_mut_ptr(), );
}
if is_fg(self.task) {
*f = (self.problem.eval_fn)(x, g)?;
} else if self.task == NEW_X as i64 {
} else {
break;
}
}
Ok(())
}
pub fn fx(&self) -> f64 {
self.problem.f
}
pub fn gx(&self) -> &[f64] {
&self.problem.g
}
pub fn x(&self) -> &[f64] {
&self.problem.x
}
}
pub fn lbfgsb<E>(x: Vec<f64>, bounds: &[(f64, f64)], eval_fn: E) -> Result<LbfgsbState<E>>
where
E: FnMut(&[f64], &mut [f64]) -> Result<f64>,
{
assert_eq!(x.len(), bounds.len());
let param = LbfgsbParameter::default();
let mut problem = LbfgsbProblem::build(x, eval_fn);
let bounds = bounds.into_iter().copied().map(|(l, u)| (Some(l), Some(u)));
problem.set_bounds(bounds);
let mut state = LbfgsbState::new(problem, param);
state.minimize()?;
Ok(state)
}