use crate::consts::{
FTARGET_ACHIEVED, FUNCMAX, INFO_DFT, MAXFUN_REACHED, NAN_INF_F, NAN_INF_X, REALMAX,
};
use crate::math;
pub(crate) fn moderatex1(v: f64) -> f64 {
let v = if v.is_nan() { 0.0 } else { v };
(-REALMAX).max(REALMAX.min(v))
}
pub(crate) fn moderatex_into(x: &[f64], y: &mut [f64]) {
debug_assert_eq!(x.len(), y.len());
for (yi, &xi) in y.iter_mut().zip(x) {
*yi = moderatex1(xi);
}
}
#[cfg(test)]
pub(crate) fn moderatex(x: &[f64]) -> Vec<f64> {
let mut y = vec![0.0; x.len()];
moderatex_into(x, &mut y);
y
}
pub(crate) fn moderatef(f: f64) -> f64 {
let mut y = f;
if y.is_nan() {
y = FUNCMAX;
}
(-REALMAX).max(FUNCMAX.min(y))
}
pub(crate) fn evaluate<F: FnMut(&[f64]) -> f64>(
calfun: &mut F,
x: &[f64],
xmod: &mut [f64],
) -> f64 {
if x.iter().any(|v| v.is_nan()) {
x.iter().sum()
} else {
moderatex_into(x, xmod);
moderatef(calfun(xmod))
}
}
pub(crate) fn checkexit(maxfun: usize, nf: usize, f: f64, ftarget: f64, x: &[f64]) -> i32 {
let mut info = INFO_DFT;
if x.iter().any(|v| v.is_nan() || v.is_infinite()) {
info = NAN_INF_X;
}
if f.is_nan() || (f.is_infinite() && f.is_sign_positive()) {
info = NAN_INF_F;
}
if f <= ftarget {
info = FTARGET_ACHIEVED;
}
if nf >= maxfun {
info = MAXFUN_REACHED;
}
info
}
pub(crate) fn xinbd_into(
xbase: &[f64],
step: &[f64],
xl: &[f64],
xu: &[f64],
sl: &[f64],
su: &[f64],
x: &mut [f64],
) {
let n = xbase.len();
for i in 0..n {
let s = sl[i].max(su[i].min(step[i]));
x[i] = xl[i].max(xu[i].min(xbase[i] + s));
if s <= sl[i] {
x[i] = xl[i];
}
if s >= su[i] {
x[i] = xu[i];
}
}
}
#[cfg(test)]
pub(crate) fn xinbd(
xbase: &[f64],
step: &[f64],
xl: &[f64],
xu: &[f64],
sl: &[f64],
su: &[f64],
) -> Vec<f64> {
let mut x = vec![0.0; xbase.len()];
xinbd_into(xbase, step, xl, xu, sl, su, &mut x);
x
}
fn linspace_into(xstart: f64, xstop: f64, n: usize, x: &mut [f64]) {
debug_assert_eq!(x.len(), n);
if n == 0 {
return;
}
let nm = n - 1;
if n == 1 || (xstart <= xstop && xstop <= xstart) {
x.fill(xstop);
} else if math::abs(xstart) <= math::abs(xstop) && math::abs(xstop) <= math::abs(xstart) {
let xunit = xstop / nm as f64;
for (idx, v) in x.iter_mut().enumerate() {
*v = xunit * (2.0 * idx as f64 - nm as f64);
}
if nm % 2 == 0 {
x[nm / 2] = 0.0;
}
} else {
let xunit = (xstop - xstart) / nm as f64;
for (i, v) in x.iter_mut().enumerate() {
*v = xstart + xunit * i as f64;
}
}
x[0] = xstart;
x[n - 1] = xstop;
}
pub(crate) fn interval_max<F: Fn(f64, &[f64]) -> f64>(
fun: F,
lb: f64,
ub: f64,
args: &[f64],
grid_size: usize,
xgrid: &mut [f64],
fgrid: &mut [f64],
) -> f64 {
if ub <= lb {
return lb;
}
linspace_into(lb, ub, grid_size, xgrid);
for (fg, &xg) in fgrid.iter_mut().zip(xgrid.iter()) {
*fg = fun(xg, args);
}
if fgrid.iter().all(|f| f.is_nan()) {
return lb;
}
let mut kopt = 0;
let mut fopt = f64::NAN;
let mut found = false;
for (k, &f) in fgrid.iter().enumerate() {
if !f.is_nan() && (!found || f > fopt) {
kopt = k;
fopt = f;
found = true;
}
}
if kopt == 0 {
lb
} else if kopt == grid_size - 1 {
ub
} else {
let fprev = fgrid[kopt - 1];
let fnext = fgrid[kopt + 1];
let mut step = 0.0;
if math::abs(fprev - fnext) > 0.0 {
step = 0.5 * ((fnext - fprev) / (fopt + fopt - fprev - fnext));
}
if step.is_finite() && math::abs(step) > 0.0 {
lb + (ub - lb) * (kopt as f64 + step) / (grid_size - 1) as f64
} else {
xgrid[kopt]
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::consts::{
FTARGET_ACHIEVED, FUNCMAX, INFO_DFT, MAXFUN_REACHED, NAN_INF_F, NAN_INF_X,
};
#[test]
fn moderatef_caps_nan_and_huge_values() {
assert_eq!(moderatef(f64::NAN), FUNCMAX);
assert_eq!(moderatef(f64::INFINITY), FUNCMAX);
assert_eq!(moderatef(1.0e40), FUNCMAX);
assert_eq!(moderatef(-f64::INFINITY), -f64::MAX);
assert_eq!(moderatef(1.5), 1.5);
}
#[test]
fn moderatex_replaces_nan_and_clamps_to_realmax() {
assert_eq!(
moderatex(&[f64::NAN, f64::INFINITY, 2.0]),
vec![0.0, f64::MAX, 2.0]
);
}
#[test]
fn evaluate_moderates_input_and_output() {
let mut xmod = [0.0; 2];
let mut nan_objective = |_: &[f64]| f64::NAN;
assert_eq!(
evaluate(&mut nan_objective, &[1.0], &mut xmod[..1]),
FUNCMAX
);
let mut sum = |x: &[f64]| x.iter().sum::<f64>();
assert_eq!(evaluate(&mut sum, &[1.0, 2.0], &mut xmod), 3.0);
assert!(evaluate(&mut sum, &[f64::NAN], &mut xmod[..1]).is_nan());
}
#[test]
fn checkexit_reports_the_prima_exit_codes_with_prima_precedence() {
assert_eq!(checkexit(100, 5, 1.0, -f64::INFINITY, &[0.0]), INFO_DFT);
assert_eq!(checkexit(100, 5, 1.0, 2.0, &[0.0]), FTARGET_ACHIEVED);
assert_eq!(
checkexit(100, 100, 1.0, -f64::INFINITY, &[0.0]),
MAXFUN_REACHED
);
assert_eq!(
checkexit(100, 5, 1.0, -f64::INFINITY, &[f64::INFINITY]),
NAN_INF_X
);
assert_eq!(
checkexit(100, 5, f64::NAN, -f64::INFINITY, &[0.0]),
NAN_INF_F
);
assert_eq!(checkexit(5, 5, 1.0, 2.0, &[0.0]), MAXFUN_REACHED);
}
#[test]
fn xinbd_pins_x_to_the_exact_bound_when_the_step_hits_it() {
let xbase = [0.5, 0.5];
let (xl, xu) = ([0.0, 0.0], [1.0, 1.0]);
let (sl, su) = ([-0.5, -0.5], [0.5, 0.5]);
let x = xinbd(&xbase, &[0.7, 0.0], &xl, &xu, &sl, &su);
assert_eq!(x, vec![1.0, 0.5]);
let x = xinbd(&xbase, &[-0.6, 0.2], &xl, &xu, &sl, &su);
assert_eq!(x, vec![0.0, 0.7]);
}
#[test]
fn interval_max_finds_the_grid_maximum() {
let f = |x: f64, _: &[f64]| x * (1.0 - x);
let (mut xgrid, mut fgrid) = (vec![0.0; 50], vec![0.0; 50]);
let x = interval_max(f, 0.0, 1.0, &[], 50, &mut xgrid, &mut fgrid);
assert!((x - 0.5).abs() <= 0.5 / 50.0 + 1e-12);
}
}