use std::error::Error;
use log::{error, warn};
use nalgebra::{DMatrix, DVector};
use num_traits::Float;
use crate::{
funcs::opt::{fminfn::fminfn, fmingr::fmingr, Optim, OptimOptions, OptimResponse},
traits::StatArray,
};
pub struct L_BFGS_B {
pub lmm: usize,
pub slower: f64,
pub supper: f64,
pub factr: f64,
pub pgtol: usize,
pub grad_func: Option<Box<dyn Fn(Vec<f64>) -> Vec<f64>>>,
pub optim_options: OptimOptions,
alpha_linesearch: f64,
beta_linesearch: f64,
max_steplength: f64,
xtol_minpack: f64,
max_iter_linesearch: usize,
}
impl Default for L_BFGS_B {
fn default() -> Self {
Self {
alpha_linesearch: 1e-4,
beta_linesearch: 0.9,
max_steplength: 1e8,
xtol_minpack: 1e-50,
max_iter_linesearch: 30,
lmm: 5,
slower: f64::neg_infinity(),
supper: f64::infinity(),
factr: 1e+07,
pgtol: 0,
grad_func: None,
optim_options: OptimOptions {
parscale: None,
fnscale: 1.0,
abstol: f64::neg_infinity(),
reltol: f64::epsilon().sqrt(),
maxit: 500,
},
}
}
}
impl Optim for L_BFGS_B {
type ParameterType = Vec<f64>;
fn optim(
&mut self,
par: Self::ParameterType,
min_func: &dyn Fn(Self::ParameterType) -> f64,
) -> Result<OptimResponse<Self::ParameterType>, Box<dyn Error>> {
let n = par.len();
let _m = self.lmm;
let parscale = if let Some(parscale) = &self.optim_options.parscale {
parscale.clone()
} else {
vec![1.0; n]
};
let mut Bvec = par
.iter()
.zip(parscale.iter())
.map(|(par, scale)| par / scale)
.collect::<Vec<_>>();
let mut par_out = par.to_vec();
let mut lower = vec![0.0; n];
let mut upper = vec![0.0; n];
let mut nbd = vec![0; n];
for i in 0..n {
lower[i] = self.slower / parscale[i];
upper[i] = self.supper / parscale[i];
if !lower[i].is_finite() {
if !upper[i].is_finite() {
nbd[i] = 0;
} else {
nbd[i] = 3;
}
} else if !upper[i].is_finite() {
nbd[i] = 1;
} else {
nbd[i] = 2;
}
}
let ret = L_BFGS_B(
&DVector::<f64>::from_row_slice(&Bvec),
min_func,
self.grad_func.as_deref(),
&DVector::<f64>::from_row_slice(&lower),
&DVector::<f64>::from_row_slice(&upper),
self.lmm,
1e-5, self.optim_options.reltol,
self.optim_options.maxit,
self.alpha_linesearch,
self.beta_linesearch,
self.max_steplength,
self.xtol_minpack,
self.max_iter_linesearch,
self.optim_options.abstol,
&parscale,
self.optim_options.fnscale,
);
for i in 0..n {
par_out[i] = ret.0[i] * parscale[i];
}
Ok(OptimResponse {
parameters: par_out,
value: ret.1,
function_count: ret.3,
gradient_count: ret.4,
convergence: ret.5,
})
}
}
fn compute_Cauchy_point(
x: &DVector<f64>,
g: &DVector<f64>,
l: &DVector<f64>,
u: &DVector<f64>,
W: &DMatrix<f64>,
M: &DMatrix<f64>,
theta: f64,
) -> (DVector<f64>, DMatrix<f64>, Vec<usize>) {
let eps_f_sec = 1e-30;
let mut t = vec![0.0; x.len()];
let mut d = DMatrix::from_element(x.len(), 1, 0.0);
let mut x_cp = x.clone();
for i in 0..x.len() {
if g[i] < 0.0 {
t[i] = (x[i] - u[i]) / g[i];
} else if g[i] > 0.0 {
t[i] = (x[i] - l[i]) / g[i];
} else {
t[i] = f64::infinity();
}
if t[i] == 0.0 {
d[i] = 0.0;
} else {
d[i] = -g[i];
}
}
let mut indices = (0..t.len()).collect::<Vec<_>>();
indices.sort_by(|&i, &j| t[i].partial_cmp(&t[j]).unwrap());
let mut F = indices
.into_iter()
.filter(|&i| t[i] > 0.0)
.collect::<Vec<_>>();
let mut t_old = 0.0;
let mut F_i = 0;
let mut b = F[0];
let mut t_min = t[b];
let mut Dt = t_min;
let mut p = W.transpose() * &d;
let mut c = DMatrix::from_element(p.nrows(), p.ncols(), 0.0);
let mut f_prime = -d.dot(&d);
let mut f_second = -theta * f_prime - p.dot(&(M * &p));
let mut f_sec0 = f_second;
let mut Dt_min = -f_prime / f_second;
let mut W_b: DVector<f64> = DVector::<f64>::zeros(1);
let mut g_b = 0.0;
while Dt_min >= Dt && F_i < F.len() {
if d[b] > 0.0 {
x_cp[b] = u[b];
} else if d[b] < 0.0 {
x_cp[b] = l[b];
}
let x_bcp = x_cp[b];
let zb = x_bcp - x[b];
c += Dt * &p;
W_b = DVector::<f64>::from_row_slice(
W.row(b).into_iter().cloned().collect::<Vec<_>>().as_slice(),
);
g_b = g[b];
f_prime += Dt * f_second + g_b * (g_b + theta * zb - W_b.dot(&(M * &c)));
f_second -= g_b * (g_b * theta + W_b.dot(&(M * (2.0 * &p + g_b * &W_b))));
f_second = f_second.min(eps_f_sec * f_sec0);
Dt_min = -f_prime / f_second;
p += g_b * W_b;
d[b] = 0.0;
t_old = t_min;
F_i += 1;
if F_i < F.len() {
b = F[F_i];
t_min = t[b];
Dt = t_min - t_old;
} else {
t_min = f64::infinity();
}
}
Dt_min = if Dt_min < 0.0 { 0.0 } else { Dt_min };
t_old += Dt_min;
for i in 0..x.len() {
if t[i] >= t_min {
x_cp[i] = x[i] + t_old * d[i];
}
}
F = F.into_iter().filter(|&i| t[i] != t_min).collect::<Vec<_>>();
c += Dt_min * p;
(x_cp, c, F)
}
fn minimize_model(
x: &DVector<f64>,
xc: &DVector<f64>,
c: &DMatrix<f64>,
g: &DVector<f64>,
l: &DVector<f64>,
u: &DVector<f64>,
W: &DMatrix<f64>,
M: &DMatrix<f64>,
theta: f64,
) -> DVector<f64> {
let invThet = 1.0 / theta;
let mut Z = vec![];
let mut free_vars = vec![];
let n = xc.len();
let mut unit = vec![0.0; n];
for i in 0..n {
unit[i] = 1.0;
if (xc[i] != u[i]) && (xc[i] != l[i]) {
free_vars.push(i);
Z.push(unit.clone());
}
unit[i] = 0.0;
}
if free_vars.is_empty() {
return xc.clone();
}
let Z = DMatrix::from_iterator(
Z.len(),
Z[0].len(),
Z.into_iter().flatten().collect::<Vec<_>>(),
)
.transpose();
let WTZ = W.transpose() * Z;
let mut not_free_vars = vec![];
for i in 0..n {
if !free_vars.contains(&i) {
not_free_vars.push(i);
}
}
let rHat = (g + theta * (xc - x) - (W * (&(M * c)))).remove_rows_at(¬_free_vars);
let mut v = &WTZ * &rHat;
v = M * v;
let mut N = invThet * (&WTZ * WTZ.transpose());
N = DMatrix::<f64>::identity(N.nrows(), N.ncols()) - (M * N);
v = N.qr().solve(&v).unwrap();
let dHat = -invThet * (&rHat + invThet * WTZ.transpose() * v);
let mut alpha_star = 1.0;
let mut idx;
for i in 0..free_vars.len() {
idx = free_vars[i];
if dHat[i] > 0.0 {
alpha_star = alpha_star.min((u[idx] - xc[idx]) / dHat[i]);
} else if dHat[i] < 0.0 {
alpha_star = alpha_star.min((l[idx] - xc[idx]) / dHat[i]);
}
}
let d_star = alpha_star * dHat;
let mut xbar = xc.clone();
for i in 0..free_vars.len() {
idx = free_vars[i];
xbar[idx] += d_star[i];
}
xbar
}
fn max_allowed_steplength(
x: &DVector<f64>,
d: &DVector<f64>,
l: &DVector<f64>,
u: &DVector<f64>,
max_steplength: f64,
) -> f64 {
let mut max_stpl = max_steplength;
for i in 0..x.len() {
if d[i] > 0.0 {
max_stpl = max_stpl.min((u[i] - x[i]) / d[i]);
} else if d[i] < 0.0 {
max_stpl = max_stpl.min((l[i] - x[i]) / d[i]);
}
}
max_stpl
}
fn dcstep(
stx: &mut f64,
fx: &mut f64,
dx: &mut f64,
sty: &mut f64,
fy: &mut f64,
dy: &mut f64,
stp: &mut f64,
fp: f64,
dp: f64,
brackt: &mut bool,
stpmin: f64,
stpmax: f64,
) {
let mut d__1;
let mut d__2;
let sgnd;
let stpc;
let mut stpf;
let stpq;
let p;
let q;
let mut gamm;
let r__;
let s;
let theta;
sgnd = dp * (*dx / dx.abs());
if fp > *fx {
theta = (*fx - fp) * 3. / (*stp - *stx) + *dx + dp;
d__1 = theta.abs();
d__2 = dx.abs();
d__1 = d__1.max(d__2);
d__2 = dp.abs();
s = d__1.max(d__2);
d__1 = theta / s;
gamm = s * (d__1 * d__1 - *dx / s * (dp / s)).sqrt();
if stp < stx {
gamm = -gamm;
}
p = gamm - *dx + theta;
q = gamm - *dx + gamm + dp;
r__ = p / q;
stpc = *stx + r__ * (*stp - *stx);
stpq = *stx + *dx / ((*fx - fp) / (*stp - *stx) + *dx) / 2.0 * (*stp - *stx);
if (stpc - *stx).abs() < (stpq - *stx).abs() {
stpf = stpc;
} else {
stpf = stpc + (stpq - stpc) / 2.0;
}
*brackt = true;
} else if sgnd < 0.0 {
theta = (*fx - fp) * 3.0 / (*stp - *stx) + *dx + dp;
d__1 = theta.abs();
d__2 = dx.abs();
d__1 = d__1.max(d__2);
d__2 = dp.abs();
s = d__1.max(d__2);
d__1 = theta / s;
gamm = s * (d__1 * d__1 - *dx / s * (dp / s)).sqrt();
if stp > stx {
gamm = -gamm;
}
p = gamm - dp + theta;
q = gamm - dp + gamm + *dx;
r__ = p / q;
stpc = *stp + r__ * (*stx - *stp);
stpq = *stp + dp / (dp - *dx) * (*stx - *stp);
if (stpc - *stp).abs() > (stpq - *stp).abs() {
stpf = stpc;
} else {
stpf = stpq;
}
*brackt = true;
} else if dp.abs() < dx.abs() {
theta = (*fx - fp) * 3.0 / (*stp - *stx) + *dx + dp;
d__1 = theta.abs();
d__2 = dx.abs();
d__1 = d__1.max(d__2);
d__2 = dp.abs();
s = d__1.max(d__2);
d__1 = theta / s;
d__1 = d__1 * d__1 - *dx / s * (dp / s);
gamm = if d__1 < 0.0 { 0.0 } else { s * d__1.sqrt() };
if *stp > *stx {
gamm = -gamm;
}
p = gamm - dp + theta;
q = gamm + (*dx - dp) + gamm;
r__ = p / q;
if r__ < 0.0 && gamm != 0.0 {
stpc = *stp + r__ * (*stx - *stp);
} else if *stp > *stx {
stpc = stpmax;
} else {
stpc = stpmin;
}
stpq = *stp + dp / (dp - *dx) * (*stx - *stp);
if *brackt {
if (stpc - *stp).abs() < (stpq - *stp).abs() {
stpf = stpc;
} else {
stpf = stpq;
}
d__1 = *stp + (*sty - *stp) * 0.66;
if stp > stx {
stpf = d__1.min(stpf);
} else {
stpf = d__1.max(stpf);
}
} else {
if (stpc - *stp).abs() > (stpq - *stp).abs() {
stpf = stpc;
} else {
stpf = stpq;
}
stpf = stpmax.min(stpf);
stpf = stpmin.max(stpf);
}
} else {
if *brackt {
theta = (fp - *fy) * 3. / (*sty - *stp) + *dy + dp;
d__1 = theta.abs();
d__2 = dy.abs();
d__1 = d__1.max(d__2);
d__2 = dp.abs();
s = d__1.max(d__2);
d__1 = theta / s;
gamm = s * (d__1 * d__1 - *dy / s * (dp / s)).sqrt();
if *stp > *sty {
gamm = -gamm;
}
p = gamm - dp + theta;
q = gamm - dp + gamm + *dy;
r__ = p / q;
stpc = *stp + r__ * (*sty - *stp);
stpf = stpc;
} else if *stp > *stx {
stpf = stpmax;
} else {
stpf = stpmin;
}
}
if fp > *fx {
*sty = *stp;
*fy = fp;
*dy = dp;
} else {
if sgnd < 0.0 {
*sty = *stx;
*fy = *fx;
*dy = *dx;
}
*stx = *stp;
*fx = fp;
*dx = dp;
}
*stp = stpf;
}
#[derive(Clone, Debug, Default)]
struct DcsrchData {
brackt: bool,
stage: usize,
finit: f64,
ginit: f64,
gtest: f64,
width: f64,
width1: f64,
stx: f64,
fx: f64,
gx: f64,
sty: f64,
fy: f64,
gy: f64,
stmin: f64,
stmax: f64,
task: String,
}
fn dcsrch(
f: f64,
g: f64,
stp: &mut f64,
ftol: f64,
gtol: f64,
xtol: f64,
stpmin: f64,
stpmax: f64,
task: &mut String,
dcsrch_data: &mut DcsrchData,
) {
let ftest;
let fm;
let gm;
let mut fxm;
let mut fym;
let mut gxm;
let mut gym;
if task.starts_with("START") {
if *stp < stpmin {
*task = "ERROR: STP .LT. STPMIN".to_string();
}
if *stp > stpmax {
*task = "ERROR: STP .GT. STPMAX".to_string();
}
if g >= 0.0 {
*task = "ERROR: INITIAL G .GE. ZERO".to_string();
}
if ftol < 0.0 {
*task = "ERROR: FTOL .LT. ZERO".to_string();
}
if gtol < 0.0 {
*task = "ERROR: GTOL .LT. ZERO".to_string();
}
if xtol < 0.0 {
*task = "ERROR: XTOL .LT. ZERO".to_string();
}
if stpmin < 0.0 {
*task = "ERROR: STPMIN .LT. ZERO".to_string();
}
if stpmax < stpmin {
*task = "ERROR: STPMAX .LT. STPMIN".to_string();
}
if task.starts_with("ERROR") {
return;
}
dcsrch_data.brackt = false;
dcsrch_data.stage = 1;
dcsrch_data.finit = f;
dcsrch_data.ginit = g;
dcsrch_data.gtest = ftol * dcsrch_data.ginit;
dcsrch_data.width = stpmax - stpmin;
dcsrch_data.width1 = dcsrch_data.width / 0.5;
dcsrch_data.stx = 0.0;
dcsrch_data.fx = dcsrch_data.finit;
dcsrch_data.gx = dcsrch_data.ginit;
dcsrch_data.sty = 0.0;
dcsrch_data.fy = dcsrch_data.finit;
dcsrch_data.gy = dcsrch_data.ginit;
dcsrch_data.stmin = 0.0;
dcsrch_data.stmax = *stp + *stp * 4.0;
*task = "FG".to_string();
return;
}
ftest = dcsrch_data.finit + *stp * dcsrch_data.gtest;
if dcsrch_data.stage == 1 && f <= ftest && g >= 0.0 {
dcsrch_data.stage = 2;
}
if dcsrch_data.brackt && (*stp < dcsrch_data.stmin || *stp > dcsrch_data.stmax) {
*task = "WARNING: ROUNDING ERRORS PREVENT PROGRESS".to_string();
}
if dcsrch_data.brackt && dcsrch_data.stmax - dcsrch_data.stmin <= xtol * dcsrch_data.stmax {
*task = "WARNING: XTOL TEST SATISFIED".to_string();
}
if *stp == stpmax && f <= ftest && g <= dcsrch_data.gtest {
*task = "WARNING: STP = STPMAX".to_string();
}
if *stp == stpmin && (f > ftest || g >= dcsrch_data.gtest) {
*task = "WARNING: STP = STPMIN".to_string();
}
if f <= ftest && g.abs() <= gtol * (-dcsrch_data.ginit) {
*task = "CONVERGENCE".to_string();
}
if task.starts_with("WARN") || task.starts_with("CONV") {
return;
}
if dcsrch_data.stage == 1 && f <= dcsrch_data.fx && f > ftest {
fm = f - *stp * dcsrch_data.gtest;
fxm = dcsrch_data.fx - dcsrch_data.stx * dcsrch_data.gtest;
fym = dcsrch_data.fy - dcsrch_data.sty * dcsrch_data.gtest;
gm = g - dcsrch_data.gtest;
gxm = dcsrch_data.gx - dcsrch_data.gtest;
gym = dcsrch_data.gy - dcsrch_data.gtest;
dcstep(
&mut dcsrch_data.stx,
&mut fxm,
&mut gxm,
&mut dcsrch_data.sty,
&mut fym,
&mut gym,
stp,
fm,
gm,
&mut dcsrch_data.brackt,
dcsrch_data.stmin,
dcsrch_data.stmax,
);
dcsrch_data.fx = fxm + dcsrch_data.stx * dcsrch_data.gtest;
dcsrch_data.fy = fym + dcsrch_data.sty * dcsrch_data.gtest;
dcsrch_data.gx = gxm + dcsrch_data.gtest;
dcsrch_data.gy = gym + dcsrch_data.gtest;
} else {
dcstep(
&mut dcsrch_data.stx,
&mut dcsrch_data.fx,
&mut dcsrch_data.gx,
&mut dcsrch_data.sty,
&mut dcsrch_data.fy,
&mut dcsrch_data.gy,
stp,
f,
g,
&mut dcsrch_data.brackt,
dcsrch_data.stmin,
dcsrch_data.stmax,
);
}
if dcsrch_data.brackt {
if (dcsrch_data.sty - dcsrch_data.stx).abs() >= dcsrch_data.width1 * 0.66 {
*stp = dcsrch_data.stx + (dcsrch_data.sty - dcsrch_data.stx) * 0.5;
}
dcsrch_data.width1 = dcsrch_data.width;
dcsrch_data.width = (dcsrch_data.sty - dcsrch_data.stx).abs();
}
if dcsrch_data.brackt {
dcsrch_data.stmin = dcsrch_data.stx.min(dcsrch_data.sty);
dcsrch_data.stmax = dcsrch_data.stx.max(dcsrch_data.sty);
} else {
dcsrch_data.stmin = *stp + (*stp - dcsrch_data.stx) * 1.1;
dcsrch_data.stmax = *stp + (*stp - dcsrch_data.stx) * 4.0;
}
if *stp < stpmin {
*stp = stpmin;
}
if *stp > stpmax {
*stp = stpmax;
}
if dcsrch_data.brackt
&& ((*stp <= dcsrch_data.stmin || *stp >= dcsrch_data.stmax)
|| (dcsrch_data.stmax - dcsrch_data.stmin <= xtol * dcsrch_data.stmax))
{
*stp = dcsrch_data.stx;
}
*task = "FG".to_string();
}
fn line_search(
x0: &DVector<f64>,
f0: f64,
g0: &DVector<f64>,
d: &DVector<f64>,
above_iter: usize,
mut max_steplength: f64,
fct_f: &dyn Fn(Vec<f64>) -> f64,
fct_grad: Option<&dyn Fn(Vec<f64>) -> Vec<f64>>,
alpha: f64, beta: f64, xtol_minpack: f64, max_iter: usize, ndeps: &[f64],
parscale: &[f64],
fnscale: f64,
bounds: Option<(Vec<f64>, Vec<f64>)>,
g_count: &mut usize,
) -> f64 {
let mut steplength_0 = if max_steplength > 1.0 {
1.0
} else {
0.5 * max_steplength
};
let mut f_m1 = f0;
let dphi = g0.dot(d);
let mut dphi_m1 = dphi;
let mut i = 0;
if above_iter == 0 {
max_steplength = 1.0;
steplength_0 = (1.0 / d.dot(d).sqrt()).min(1.0);
}
let mut task = "START".to_string();
let mut steplength = f64::nan();
let mut dcsrch_data = DcsrchData::default();
while i < max_iter {
steplength = steplength_0;
dcsrch(
f_m1,
dphi_m1,
&mut steplength,
alpha,
beta,
xtol_minpack,
0.0,
max_steplength,
&mut task,
&mut dcsrch_data,
);
if task.starts_with("FG") {
steplength_0 = steplength;
f_m1 = fct_f((x0 + steplength * d).as_slice().to_vec());
dphi_m1 = DVector::<f64>::from_vec(
fmingr(
x0.len(),
(x0 + steplength * d).as_slice(),
&ndeps,
parscale,
fnscale,
bounds.clone(),
fct_f,
fct_grad,
)
.1,
)
.dot(d);
*g_count += 1;
} else {
break;
}
i += 1;
}
if i >= max_iter {
println!("Max iter reached");
steplength = f64::nan()
}
if (task.starts_with("ERROR") || task.starts_with("WARN")) && task != "WARNING: STP = STPMAX" {
println!("{}", task);
steplength = f64::nan(); }
steplength
}
fn update_SY(
sk: &DVector<f64>,
yk: &DVector<f64>,
S: &mut Vec<DVector<f64>>,
Y: &mut Vec<DVector<f64>>,
m: usize,
eps: f64, ) -> Option<(DMatrix<f64>, DMatrix<f64>, f64)> {
let sTy = sk.dot(yk);
let yTy = yk.dot(yk);
if sTy > eps * yTy {
S.push(sk.clone());
Y.push(yk.clone());
if S.len() > m {
S.remove(0);
Y.remove(0);
}
let mut SArray = DMatrix::<f64>::zeros(0, S[0].nrows());
let mut YArray = DMatrix::<f64>::zeros(0, S[0].nrows());
for i in 0..S.len() {
SArray = SArray.clone().insert_row(0, 0.0);
SArray
.row_mut(i)
.iter_mut()
.zip(sk.iter())
.for_each(|(s, &sk)| *s = sk);
YArray = YArray.clone().insert_row(0, 0.0);
YArray
.row_mut(i)
.iter_mut()
.zip(yk.iter())
.for_each(|(y, &yk)| *y = yk);
}
SArray = SArray.transpose();
YArray = YArray.transpose();
let STS = &SArray.transpose() * &SArray;
let mut L = &SArray.transpose() * &YArray;
let mut D = DMatrix::<f64>::zeros(L.nrows(), L.ncols());
D.set_diagonal(&-L.diagonal());
L.fill_diagonal(0.0);
L = L.lower_triangle();
let hstack = |X: &DMatrix<f64>, Y: &DMatrix<f64>| -> DMatrix<f64> {
let mut Z = X
.clone()
.resize(X.nrows(), X.ncols() + Y.ncols(), f64::nan());
for i in 0..X.nrows() {
for j in 0..Y.ncols() {
*Z.get_mut((i, X.ncols() + j)).unwrap() = Y[(i, j)];
}
}
Z
};
let vstack = |X: &DMatrix<f64>, Y: &DMatrix<f64>| -> DMatrix<f64> {
let mut Z = X
.clone()
.resize(X.nrows() + Y.nrows(), X.ncols(), f64::nan());
for i in 0..Y.nrows() {
for j in 0..X.ncols() {
*Z.get_mut((X.nrows() + i, j)).unwrap() = Y[(i, j)];
}
}
Z
};
let thet = yTy / sTy;
let W = hstack(&YArray, &(thet * SArray));
let M = hstack(&vstack(&D, &L), &vstack(&L.transpose(), &(thet * STS)))
.pseudo_inverse(0.0)
.unwrap();
Some((W, M, thet))
} else {
None
}
}
pub fn L_BFGS_B(
x0: &DVector<f64>,
f: &dyn Fn(Vec<f64>) -> f64,
df: Option<&dyn Fn(Vec<f64>) -> Vec<f64>>,
l: &DVector<f64>,
u: &DVector<f64>,
m: usize,
epsg: f64,
epsf: f64,
max_iter: usize,
alpha_linesearch: f64,
beta_linesearch: f64,
max_steplength: f64,
xtol_minpack: f64,
max_iter_linesearch: usize,
eps_SY: f64,
parscale: &[f64],
fnscale: f64,
) -> (DVector<f64>, f64, DVector<f64>, usize, usize, bool) {
let clip = |x: &mut [f64], l: &[f64], u: &[f64]| {
for ((x_i, &l_i), &u_i) in x.iter_mut().zip(l.iter()).zip(u.iter()) {
if *x_i < l_i {
*x_i = l_i;
} else if *x_i > u_i {
*x_i = u_i;
}
}
};
let n = x0.len();
let ndeps = vec![1e-3; n];
let mut x = x0.clone();
clip(x.as_mut_slice(), l.as_slice(), u.as_slice());
let mut S = vec![];
let mut Y = vec![];
let mut W = DMatrix::<f64>::zeros(n, 1);
let mut M = DMatrix::<f64>::zeros(1, 1);
let mut theta = 1.0;
let epsmch = f64::epsilon();
let bounds = Some((l.as_slice().to_vec(), u.as_slice().to_vec()));
let mut f0 = fminfn(n, x.as_slice(), parscale, fnscale, f);
let mut f_count = 1;
let mut g = DVector::<f64>::from_vec(
fmingr(
n,
x.as_slice(),
&ndeps,
parscale,
fnscale,
bounds.clone(),
f,
df,
)
.1,
);
let mut g_count = 1;
let mut i = 0;
let get_x_max = |x: &[f64], g: &DVector<f64>, l: &DVector<f64>, u: &DVector<f64>| {
x.iter()
.zip(g.iter())
.zip(l.iter())
.zip(u.iter())
.map(|(((x_i, g_i), &l_i), &u_i)| {
let mut x_temp = x_i - g_i;
if x_temp < l_i {
x_temp = l_i;
} else if x_temp > u_i {
x_temp = u_i;
}
(x_temp - x_i).abs()
})
.max_by(|x_1, x_2| x_1.partial_cmp(x_2).unwrap())
.unwrap()
};
while get_x_max(x.as_slice(), &g, l, u) > epsg && i < max_iter {
let oldf0 = f0;
let oldx = x.clone();
let oldg = g.clone();
let dictCP = compute_Cauchy_point(&x, &g, l, u, &W, &M, theta);
let dictMinMod = minimize_model(&x, &dictCP.0, &dictCP.1, &g, l, u, &W, &M, theta);
let d = dictMinMod - &x;
let max_stpl = max_allowed_steplength(&x, &d, &l, &u, max_steplength);
let steplength = line_search(
&x,
f0,
&g,
&d,
i,
max_stpl,
f,
df,
alpha_linesearch,
beta_linesearch,
xtol_minpack,
max_iter_linesearch,
&ndeps,
parscale,
fnscale,
bounds.clone(),
&mut g_count,
);
if steplength.is_nan() {
if S.is_empty() {
error!("Error: can not compute new steplength : abort");
return (
x.clone(),
f(x.as_slice().to_vec()),
DVector::<f64>::from_vec(
fmingr(
n,
x.as_slice(),
&ndeps,
parscale,
fnscale,
bounds.clone(),
f,
df,
)
.1,
),
f_count,
g_count,
false,
);
} else {
S = vec![];
Y = vec![];
W = DMatrix::<f64>::zeros(n, 1);
M = DMatrix::<f64>::zeros(1, 1);
theta = 1.0;
}
} else {
x += steplength * d;
f0 = fminfn(n, x.as_slice(), parscale, fnscale, f);
f_count += 1;
g = DVector::<f64>::from_vec(
fmingr(
n,
x.as_slice(),
&ndeps,
parscale,
fnscale,
bounds.clone(),
f,
df,
)
.1,
);
g_count += 1;
if let Some((W_t, M_t, theta_t)) =
update_SY(&(&x - oldx), &(&g - oldg), &mut S, &mut Y, m, eps_SY)
{
W = W_t;
M = M_t;
theta = theta_t;
}
if (oldf0 - f0) / vec![oldf0.abs(), f0.abs(), 1.0].max() < epsmch * epsf {
error!("Relative reduction of f below tolerence: abort.");
break;
}
i += 1;
}
}
if i == max_iter {
warn!("Maximum iteration reached.");
}
(x, f0, g, f_count, g_count, i < max_iter)
}