use crate::core::math::Scalar;
use crate::solver::powell::QuadraticModel;
fn denominators<F: Scalar>(model: &QuadraticModel<F>, d: &[F]) -> Vec<F> {
let n = model.n();
let kopt = model.kopt();
let xopt = model.xpt_row(kopt).to_vec();
let xnew_disp: Vec<F> = (0..n).map(|i| xopt[i] + d[i]).collect();
let ctx = model.prepare_update(&xnew_disp);
(0..model.m())
.map(|k| model.update_params(k, &ctx).sigma)
.collect()
}
fn distsq_to<F: Scalar>(model: &QuadraticModel<F>, center: &[F]) -> Vec<F> {
let n = model.n();
(0..model.m())
.map(|k| {
let row = model.xpt_row(k);
(0..n).fold(F::zero(), |a, i| {
a + (row[i] - center[i]) * (row[i] - center[i])
})
})
.collect()
}
pub(crate) fn setdrop_tr<F: Scalar>(
model: &QuadraticModel<F>,
ximproved: bool,
d: &[F],
rho: F,
) -> Option<usize> {
let n = model.n();
let kopt = model.kopt();
let one = F::one();
let zero = F::zero();
let center: Vec<F> = if ximproved {
let xopt = model.xpt_row(kopt);
(0..n).map(|i| xopt[i] + d[i]).collect()
} else {
model.xpt_row(kopt).to_vec()
};
let distsq = distsq_to(model, ¢er);
let rho2 = rho * rho;
let den = denominators(model, d);
let mut score = vec![zero; model.m()];
for k in 0..model.m() {
let w = (distsq[k] / rho2).max(one);
let w4 = w * w * w * w;
score[k] = w4 * den[k];
}
if !ximproved {
score[kopt] = -one;
}
let any_gt1 = score.iter().any(|&s| s > one);
let any_gt0 = score.iter().any(|&s| s > zero);
if any_gt1 || (ximproved && any_gt0) {
let mut knew = 0;
let mut best = F::neg_infinity();
for k in 0..model.m() {
if !score[k].is_nan() && score[k] > best {
best = score[k];
knew = k;
}
}
Some(knew)
} else if ximproved {
let mut knew = 0;
let mut best = F::neg_infinity();
for k in 0..model.m() {
if !distsq[k].is_nan() && distsq[k] > best {
best = distsq[k];
knew = k;
}
}
Some(knew)
} else {
None
}
}
fn sign_of<F: Scalar>(a: F, b: F) -> F {
if b < F::zero() { -a.abs() } else { a.abs() }
}
pub(crate) fn geostep<F: Scalar>(
model: &QuadraticModel<F>,
knew: usize,
delbar: F,
sl: &[F],
su: &[F],
) -> Vec<F> {
let n = model.n();
let m = model.m();
let kopt = model.kopt();
let zero = F::zero();
let one = F::one();
let two = F::from_f64(2.0).unwrap();
let half = F::from_f64(0.5).unwrap();
let xopt = model.xpt_row(kopt).to_vec();
let (g0, pqlag) = model.lagrange_coeffs(knew);
let alpha = pqlag[knew];
let hxopt = model.lagrange_hessian_matvec(&pqlag, &xopt);
let mut glag: Vec<F> = (0..n).map(|i| g0[i] + hxopt[i]).collect();
if glag.iter().any(|v| v.is_nan()) {
let d: Vec<F> = (0..n).map(|i| model.xpt_row(knew)[i] - xopt[i]).collect();
let dn = (0..n).fold(zero, |a, i| a + d[i] * d[i]).sqrt();
let scale = half.min(delbar / dn);
return (0..n).map(|i| scale * d[i]).collect();
}
let distsq = distsq_to(model, &xopt);
let dderiv: Vec<F> = (0..m)
.map(|k| {
let row = model.xpt_row(k);
(0..n).fold(zero, |a, i| a + glag[i] * (row[i] - xopt[i]))
})
.collect();
let mut stplen = vec![[zero; 3]; m];
let mut isbd = vec![[0isize; 3]; m];
let mut dderiv = dderiv;
for k in 0..m {
if k == kopt || dderiv[k].is_nan() {
dderiv[k] = zero;
continue;
}
let dk = distsq[k].sqrt();
let mut subd = delbar / dk;
let mut slbd = -subd;
let mut ilbd = 0isize;
let mut iubd = 0isize;
let sumin = one.min(subd);
let row = model.xpt_row(k);
let xdiff: Vec<F> = (0..n).map(|i| row[i] - xopt[i]).collect();
let mut lfrac = vec![zero; n];
let mut ufrac = vec![zero; n];
for i in 0..n {
lfrac[i] = sign_of(subd, -xdiff[i]);
if sl[i] - xopt[i] > -xdiff[i].abs() * subd {
lfrac[i] = (sl[i] - xopt[i]) / xdiff[i];
}
ufrac[i] = sign_of(subd, xdiff[i]);
if su[i] - xopt[i] < xdiff[i].abs() * subd {
ufrac[i] = (su[i] - xopt[i]) / xdiff[i];
}
}
let mut best = slbd;
let mut iarg: Option<usize> = None;
for i in 0..n {
let cand = if xdiff[i] > zero {
lfrac[i]
} else if xdiff[i] < zero {
ufrac[i]
} else {
slbd
};
if !cand.is_nan() && cand > best {
best = cand;
iarg = Some(i);
}
}
if let Some(i) = iarg {
slbd = best;
ilbd = -((i as isize) + 1) * (if xdiff[i] < zero { -1 } else { 1 });
}
let mut best = subd;
let mut iarg: Option<usize> = None;
for i in 0..n {
let cand = if xdiff[i] > zero {
ufrac[i]
} else if xdiff[i] < zero {
lfrac[i]
} else {
subd
};
if !cand.is_nan() && cand < best {
best = cand;
iarg = Some(i);
}
}
if let Some(i) = iarg {
subd = sumin.max(best);
iubd = ((i as isize) + 1) * (if xdiff[i] < zero { -1 } else { 1 });
}
let mut stpm = half;
if k == knew {
stpm = slbd;
if (one - dderiv[k]).abs() > zero {
stpm = -half * dderiv[k] / (one - dderiv[k]);
}
}
stpm = slbd.max(subd.min(stpm));
stplen[k] = [slbd, subd, stpm];
isbd[k] = [ilbd, iubd, 0];
}
let mut best_pred = F::neg_infinity();
let mut best_isq = 0usize;
let mut best_ksq = kopt;
for k in 0..m {
if k == kopt {
continue;
}
for isq in 0..3 {
let t = stplen[k][isq];
let vlag = if k == knew {
t * (t * (one - dderiv[k]) + dderiv[k])
} else {
t * (one - t) * dderiv[k]
};
let vlag = if vlag.is_nan() { zero } else { vlag };
let bb = half * (t * (one - t) * distsq[k]);
let betabd = bb * bb;
let mut predsq = vlag * vlag * (vlag * vlag + alpha * betabd);
if predsq.is_nan() {
predsq = zero;
}
if predsq > best_pred {
best_pred = predsq;
best_isq = isq;
best_ksq = k;
}
}
}
let stpsiz = stplen[best_ksq][best_isq];
let ibd = isbd[best_ksq][best_isq];
let row = model.xpt_row(best_ksq);
let mut xline: Vec<F> = (0..n)
.map(|i| sl[i].max(su[i].min(xopt[i] + stpsiz * (row[i] - xopt[i]))))
.collect();
if ibd < 0 {
xline[(-ibd) as usize - 1] = sl[(-ibd) as usize - 1];
} else if ibd > 0 {
xline[ibd as usize - 1] = su[ibd as usize - 1];
}
let d_line: Vec<F> = (0..n).map(|i| xline[i] - xopt[i]).collect();
if delbar > F::from_f64(1e-2).unwrap() {
return d_line;
}
let den_line_knew = denominators(model, &d_line)[knew];
let bigstp = delbar + delbar;
let mut xcauchy = xopt.clone();
let mut vlagsq_cauchy = zero;
for uphill in 0..2 {
if uphill == 1 {
for v in glag.iter_mut() {
*v = -*v;
}
}
let mut s = vec![zero; n];
let mut mask_free = vec![false; n];
for i in 0..n {
mask_free[i] =
(xopt[i] - sl[i]).min(glag[i]) > zero || (xopt[i] - su[i]).max(glag[i]) < zero;
if mask_free[i] {
s[i] = bigstp;
}
}
let mut ggfree = (0..n).fold(zero, |a, i| {
if mask_free[i] {
a + glag[i] * glag[i]
} else {
a
}
});
if ggfree <= zero {
continue;
}
let mut sfixsq = zero;
let mut grdstp = zero;
for _ in 0..n {
let resis = delbar * delbar - sfixsq;
if resis <= zero {
break;
}
let ssqsav = sfixsq;
grdstp = (resis / ggfree).sqrt();
for i in 0..n {
if s[i] >= bigstp {
let xtemp = xopt[i] - grdstp * glag[i];
if xtemp <= sl[i] {
s[i] = sl[i] - xopt[i];
sfixsq = sfixsq + s[i] * s[i];
} else if xtemp >= su[i] {
s[i] = su[i] - xopt[i];
sfixsq = sfixsq + s[i] * s[i];
}
}
}
ggfree = (0..n).fold(zero, |a, i| {
if s[i] >= bigstp {
a + glag[i] * glag[i]
} else {
a
}
});
if !(sfixsq > ssqsav && ggfree > zero) {
break;
}
}
let mut x = vec![zero; n];
for i in 0..n {
x[i] = if glag[i] > zero { sl[i] } else { su[i] };
if s[i].abs() <= zero {
x[i] = xopt[i];
}
if s[i] >= bigstp {
x[i] = sl[i].max(su[i].min(xopt[i] - grdstp * glag[i]));
s[i] = -grdstp * glag[i];
}
}
let gs = (0..n).fold(zero, |a, i| a + glag[i] * s[i]);
let mut curv = {
let hs = model.lagrange_hessian_matvec(&pqlag, &s);
(0..n).fold(zero, |a, i| a + s[i] * hs[i])
};
if uphill == 1 {
curv = -curv;
}
let vlagsq;
if curv > -gs && curv < -(one + two.sqrt()) * gs {
let scaling = -gs / curv;
for i in 0..n {
x[i] = sl[i].max(su[i].min(xopt[i] + scaling * s[i]));
}
let v = half * gs * scaling;
vlagsq = v * v;
} else {
let v = gs + half * curv;
vlagsq = v * v;
}
if vlagsq > vlagsq_cauchy {
xcauchy = x;
vlagsq_cauchy = vlagsq;
}
}
let d_cauchy: Vec<F> = (0..n).map(|i| xcauchy[i] - xopt[i]).collect();
let den_cauchy_knew = denominators(model, &d_cauchy)[knew];
if den_cauchy_knew > den_line_knew.max(zero) || den_line_knew.is_nan() {
d_cauchy
} else {
d_line
}
}