use crate::consts::{INFO_DFT, REALMAX};
use crate::linalg::matprod21_into;
use crate::mat::Mat;
use crate::powalg::setij_into;
use crate::util::{checkexit, evaluate, xinbd_into};
#[derive(Debug, Clone)]
pub(crate) struct InitWs {
evaluated: Vec<bool>, x: Vec<f64>, xmod: Vec<f64>, xa: Vec<f64>, xb: Vec<f64>, shift: Vec<f64>, }
impl InitWs {
pub(crate) fn new(n: usize, npt: usize) -> Self {
let ndiag = n.min(npt - n - 1);
Self {
evaluated: vec![false; npt],
x: vec![0.0; n],
xmod: vec![0.0; n],
xa: vec![0.0; ndiag],
xb: vec![0.0; ndiag],
shift: vec![0.0; n],
}
}
}
#[expect(clippy::too_many_arguments)] pub(crate) fn initxf<F: FnMut(&[f64]) -> f64>(
calfun: &mut F,
maxfun: usize,
ftarget: f64,
rhobeg: f64,
xl: &[f64],
xu: &[f64],
x0: &mut [f64],
ij: &mut Vec<(usize, usize)>,
fval: &mut [f64],
sl: &mut [f64],
su: &mut [f64],
xbase: &mut [f64],
xpt: &mut Mat,
ws: &mut InitWs,
) -> (usize, usize, i32) {
let n = xpt.nrows();
let npt = xpt.ncols();
let InitWs {
evaluated, x, xmod, ..
} = ws;
let mut info = INFO_DFT;
for i in 0..n {
sl[i] = xl[i] - x0[i];
su[i] = xu[i] - x0[i];
}
for i in 0..n {
if sl[i] < 0.0 {
sl[i] = sl[i].min(-rhobeg);
} else {
x0[i] = xl[i];
sl[i] = 0.0;
su[i] = xu[i] - xl[i];
}
}
for i in 0..n {
if su[i] > 0.0 {
su[i] = su[i].max(rhobeg);
} else {
x0[i] = xu[i];
sl[i] = xl[i] - xu[i];
su[i] = 0.0;
}
}
xbase.copy_from_slice(x0);
evaluated.fill(false);
fval.fill(REALMAX);
xpt.fill(0.0);
for k in 0..n {
xpt[[k, k + 1]] = rhobeg;
if su[k] <= 0.0 {
xpt[[k, k + 1]] = -rhobeg;
}
}
for k in 0..(npt - n - 1).min(n) {
xpt[[k, k + n + 1]] = -rhobeg;
if sl[k] >= 0.0 {
xpt[[k, k + n + 1]] = (2.0 * rhobeg).min(su[k]);
}
if su[k] <= 0.0 {
xpt[[k, k + n + 1]] = (-2.0 * rhobeg).max(sl[k]);
}
}
for k in 0..npt.min(2 * n + 1) {
xinbd_into(xbase, xpt.col(k), xl, xu, sl, su, x);
let f = evaluate(calfun, x, xmod);
evaluated[k] = true;
fval[k] = f;
let subinfo = checkexit(maxfun, k + 1, f, ftarget, x);
if subinfo != INFO_DFT {
info = subinfo;
break;
}
}
for k in 1..(npt - n).min(n + 1) {
if xpt[[k - 1, k]] * xpt[[k - 1, k + n]] < 0.0 && fval[k + n] < fval[k] {
fval.swap(k, k + n);
for r in 0..n {
let tmp = xpt[[r, k]];
xpt[[r, k]] = xpt[[r, k + n]];
xpt[[r, k + n]] = tmp;
}
}
}
setij_into(n, npt, ij);
for (k, &(i, j)) in ij.iter().enumerate() {
for r in 0..n {
xpt[[r, 2 * n + 1 + k]] = xpt[[r, i + 1]] + xpt[[r, j + 1]];
}
}
if info == INFO_DFT {
for k in (2 * n + 1)..npt {
xinbd_into(xbase, xpt.col(k), xl, xu, sl, su, x);
let f = evaluate(calfun, x, xmod);
evaluated[k] = true;
fval[k] = f;
let subinfo = checkexit(maxfun, k + 1, f, ftarget, x);
if subinfo != INFO_DFT {
info = subinfo;
break;
}
}
}
let nf = evaluated.iter().filter(|&&e| e).count();
let mut kopt = 0;
let mut found = false;
for k in 0..npt {
if evaluated[k] && (!found || fval[k] < fval[kopt]) {
kopt = k;
found = true;
}
}
(kopt, nf, info)
}
pub(crate) fn initq(
ij: &[(usize, usize)],
fval: &[f64],
xpt: &Mat,
gopt: &mut [f64],
hq: &mut Mat,
pq: &mut [f64],
ws: &mut InitWs,
) -> i32 {
let n = xpt.nrows();
let npt = xpt.ncols();
let InitWs { xa, xb, shift, .. } = ws;
let fbase = fval[0];
for i in 0..n {
gopt[i] = (fval[i + 1] - fbase) / xpt[[i, i + 1]];
}
let ndiag = n.min(npt - n - 1);
xa.fill(0.0);
xb.fill(0.0);
for k in 0..ndiag {
xa[k] = xpt[[k, k + 1]]; xb[k] = xpt[[k, n + 1 + k]]; }
for k in 0..ndiag {
gopt[k] = (gopt[k] * xb[k] - ((fval[n + 1 + k] - fbase) / xb[k]) * xa[k]) / (xb[k] - xa[k]);
}
hq.fill(0.0);
for k in 0..ndiag {
hq[[k, k]] = 2.0 * ((fval[k + 1] - fbase) / xa[k] - (fval[n + k + 1] - fbase) / xb[k])
/ (xa[k] - xb[k]);
}
for (k, &(i, j)) in ij.iter().enumerate() {
let xi = xpt[[i, 2 * n + 1 + k]];
let xj = xpt[[j, 2 * n + 1 + k]];
hq[[i, j]] = (fbase - fval[i + 1] - fval[j + 1] + fval[2 * n + 1 + k]) / (xi * xj);
hq[[j, i]] = hq[[i, j]];
}
let mut kopt = 0;
for k in 0..npt {
if fval[k] < fval[kopt] {
kopt = k;
}
}
if kopt != 0 {
matprod21_into(hq, xpt.col(kopt), shift);
for i in 0..n {
gopt[i] += shift[i];
}
}
pq.fill(0.0);
if gopt.iter().any(|v| v.is_nan()) || hq.data().iter().any(|v| v.is_nan()) {
crate::consts::NAN_INF_MODEL
} else {
INFO_DFT
}
}
pub(crate) fn inith(
ij: &[(usize, usize)],
xpt: &Mat,
bmat: &mut Mat,
zmat: &mut Mat,
ws: &mut InitWs,
) -> i32 {
let n = xpt.nrows();
let npt = xpt.ncols();
let InitWs { xa, xb, .. } = ws;
let mut rhobeg = 0.0_f64;
for &v in xpt.col(1) {
rhobeg = rhobeg.max(crate::math::abs(v));
}
let rhosq = rhobeg * rhobeg;
let ndiag = n.min(npt - n - 1);
xa.fill(0.0);
xb.fill(0.0);
for k in 0..ndiag {
xa[k] = xpt[[k, k + 1]]; xb[k] = xpt[[k, n + 1 + k]]; }
bmat.fill(0.0);
for k in 0..ndiag {
bmat[[k, 0]] = -(xa[k] + xb[k]) / (xa[k] * xb[k]);
}
for k in 0..ndiag {
bmat[[k, k + n + 1]] = -0.5 / xpt[[k, k + 1]];
bmat[[k, k + 1]] = -bmat[[k, 0]] - bmat[[k, k + n + 1]];
}
for k in ndiag..n {
bmat[[k, 0]] = -1.0 / xpt[[k, k + 1]];
bmat[[k, k + 1]] = -bmat[[k, 0]];
bmat[[k, npt + k]] = -0.5 * rhosq;
}
zmat.fill(0.0);
for k in 0..ndiag {
zmat[[0, k]] = crate::math::sqrt(2.0) / (xa[k] * xb[k]);
}
for k in 0..ndiag {
zmat[[k + 1, k]] = -zmat[[0, k]] - crate::math::sqrt(0.5) / rhosq;
zmat[[k + n + 1, k]] = crate::math::sqrt(0.5) / rhosq;
}
for k in ndiag..(npt - n - 1) {
zmat[[0, k]] = 1.0 / rhosq;
zmat[[k + n + 1, k]] = 1.0 / rhosq;
let (i, j) = ij[k - n];
zmat[[i + 1, k]] = -1.0 / rhosq;
zmat[[j + 1, k]] = -1.0 / rhosq;
}
if bmat.data().iter().any(|v| v.is_nan()) || zmat.data().iter().any(|v| v.is_nan()) {
crate::consts::NAN_INF_MODEL
} else {
INFO_DFT
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mat::Mat;
use crate::test_support::{self, DiffStats};
#[test]
fn initxf_matches_prima_on_every_captured_state() {
let corpus = test_support::load_states("initxf");
assert!(!corpus.is_empty());
let mut stats = DiffStats::default();
for st in &corpus {
let (e, x) = (&st.entry, &st.exit);
let (xl, xu) = (e.vec("xl"), e.vec("xu"));
let mut x0 = e.vec("x0");
let n = xl.len();
let npt = x.vec("fval").len();
let f = test_support::objective(&st.problem);
let mut ij = Vec::new();
let mut fval = vec![0.0; npt];
let (mut sl, mut su, mut xbase) = (vec![0.0; n], vec![0.0; n], vec![0.0; n]);
let mut xpt = Mat::zeros(n, npt);
let mut ws = InitWs::new(n, npt);
let (kopt, nf, info) = initxf(
&mut |p: &[f64]| f(p),
e.usize("maxfun"),
e.f64("ftarget"),
e.f64("rhobeg"),
&xl,
&xu,
&mut x0,
&mut ij,
&mut fval,
&mut sl,
&mut su,
&mut xbase,
&mut xpt,
&mut ws,
);
assert_eq!(kopt + 1, x.usize("kopt"), "{}: kopt", st.problem);
assert_eq!(nf, x.usize("nf"), "{}: nf", st.problem);
assert_eq!(i64::from(info), x.i64("info"), "{}: info", st.problem);
assert_eq!(ij, x.ij("ij"), "{}: ij", st.problem);
stats.slice("x0", &x0, &x.vec("x0"));
stats.slice("fval", &fval, &x.vec("fval"));
stats.slice("sl", &sl, &x.vec("sl"));
stats.slice("su", &su, &x.vec("su"));
stats.slice("xbase", &xbase, &x.vec("xbase"));
stats.mat("xpt", &xpt, &x.mat("xpt"));
}
stats.report("initxf");
}
#[test]
fn initq_matches_prima_on_every_captured_state() {
let corpus = test_support::load_states("initq");
assert!(!corpus.is_empty());
let mut stats = DiffStats::default();
for st in &corpus {
let (e, x) = (&st.entry, &st.exit);
let ij = e.ij("ij");
let fval = e.vec("fval");
let xpt = e.mat("xpt");
let n = xpt.nrows();
let mut gopt = vec![0.0; n];
let mut hq = Mat::zeros(n, n);
let mut pq = vec![0.0; fval.len()];
let mut ws = InitWs::new(xpt.nrows(), xpt.ncols());
let _info = initq(&ij, &fval, &xpt, &mut gopt, &mut hq, &mut pq, &mut ws);
stats.slice("gopt", &gopt, &x.vec("gopt"));
stats.mat("hq", &hq, &x.mat("hq"));
stats.slice("pq", &pq, &x.vec("pq"));
}
stats.report("initq");
}
#[test]
fn inith_matches_prima_on_every_captured_state() {
let corpus = test_support::load_states("inith");
assert!(!corpus.is_empty());
let mut stats = DiffStats::default();
for st in &corpus {
let (e, x) = (&st.entry, &st.exit);
let ij = e.ij("ij");
let xpt = e.mat("xpt");
let (n, npt) = (xpt.nrows(), xpt.ncols());
let mut bmat = Mat::zeros(n, npt + n);
let mut zmat = Mat::zeros(npt, npt - n - 1);
let mut ws = InitWs::new(n, npt);
let _info = inith(&ij, &xpt, &mut bmat, &mut zmat, &mut ws);
stats.mat("bmat", &bmat, &x.mat("bmat"));
stats.mat("zmat", &zmat, &x.mat("zmat"));
}
stats.report("inith");
}
}