use crate::error::OptimizeResult;
use crate::result::OptimizeResults;
use scirs2_core::ndarray::{s, Array1, Array2};
pub mod bobyqa;
pub mod nelder_mead;
pub mod pattern_search;
pub use bobyqa::{BOBYQAOptions, BOBYQASolver};
pub use nelder_mead::{NelderMeadOptions, NelderMeadSolver};
pub use pattern_search::{PatternSearchOptions, PatternSearchSolver};
#[derive(Debug, Clone)]
pub struct DfOptResult {
pub x: Array1<f64>,
pub fun: f64,
pub nfev: usize,
pub nit: usize,
pub success: bool,
pub message: String,
}
impl From<DfOptResult> for OptimizeResults<f64> {
fn from(r: DfOptResult) -> Self {
OptimizeResults {
x: r.x,
fun: r.fun,
jac: None,
hess: None,
constr: None,
nit: r.nit,
nfev: r.nfev,
njev: 0,
nhev: 0,
maxcv: 0,
message: r.message,
success: r.success,
status: if r.success { 0 } else { 1 },
}
}
}
pub trait DerivativeFreeOptimizer {
fn minimize<F>(&self, func: F, x0: &[f64]) -> OptimizeResult<DfOptResult>
where
F: Fn(&[f64]) -> f64;
}
pub(crate) fn centroid(simplex: &Array2<f64>, n: usize) -> Array1<f64> {
let mut c = Array1::zeros(n);
for i in 0..n {
c += &simplex.slice(s![i, ..]);
}
c / n as f64
}
pub(crate) fn norm(v: &[f64]) -> f64 {
v.iter().map(|x| x * x).sum::<f64>().sqrt()
}
#[allow(dead_code)]
pub(crate) fn finite_diff_grad<F: Fn(&[f64]) -> f64>(f: &F, x: &[f64], h: f64) -> Vec<f64> {
let n = x.len();
let mut g = vec![0.0; n];
let mut xp = x.to_vec();
let mut xm = x.to_vec();
for i in 0..n {
xp[i] += h;
xm[i] -= h;
g[i] = (f(&xp) - f(&xm)) / (2.0 * h);
xp[i] = x[i];
xm[i] = x[i];
}
g
}
#[inline]
pub(crate) fn clip(v: f64, lo: f64, hi: f64) -> f64 {
v.max(lo).min(hi)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_centroid_2d() {
let mut s = Array2::zeros((3, 2));
s[[0, 0]] = 0.0;
s[[0, 1]] = 0.0;
s[[1, 0]] = 1.0;
s[[1, 1]] = 0.0;
s[[2, 0]] = 0.0;
s[[2, 1]] = 1.0;
let c = centroid(&s, 2);
assert!((c[0] - 0.5).abs() < 1e-12);
assert!((c[1] - 0.0).abs() < 1e-12);
}
#[test]
fn test_norm() {
let v = vec![3.0_f64, 4.0];
assert!((norm(&v) - 5.0).abs() < 1e-12);
}
}