use std::{error::Error, ops::DerefMut};
use num_traits::Float;
use r2rs_nmath::{rng::NMathRNG, traits::RNG};
use crate::funcs::opt::{fminfn::fminfn, Optim, OptimOptions, OptimResponse};
pub struct SANN<R: RNG> {
pub tmax: usize,
pub ti: f64,
pub rng: Box<R>,
pub grad_func: Option<Box<dyn Fn(Vec<f64>) -> Vec<f64>>>,
pub optim_options: OptimOptions,
}
impl<R: RNG + Default> Default for SANN<R> {
fn default() -> Self {
Self {
tmax: 10,
ti: 10.0,
rng: Box::<R>::default(),
grad_func: None,
optim_options: OptimOptions {
parscale: None,
fnscale: 1.0,
abstol: f64::neg_infinity(),
reltol: f64::epsilon().sqrt(),
maxit: 10_000,
},
}
}
}
impl<R: RNG> Optim for SANN<R> {
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 npar = par.len();
let parscale = if let Some(parscale) = &self.optim_options.parscale {
parscale.clone()
} else {
vec![1.0; npar]
};
let mut bvec = par
.iter()
.zip(parscale.iter())
.map(|(par, scale)| par / scale)
.collect::<Vec<_>>();
let mut grcount = 0;
let mut fncount = 0;
let mut min = f64::nan();
let mut par_out = Vec::new();
min = samin(
npar,
&mut bvec,
min_func,
self.grad_func.as_deref(),
self.optim_options.maxit,
self.tmax,
&parscale,
self.optim_options.fnscale,
self.ti,
self.rng.deref_mut(),
);
fncount = if npar > 0 {
self.optim_options.maxit
} else {
1
};
par_out = bvec.clone();
for i in 0..npar {
par_out[i] *= parscale[i];
}
Ok(OptimResponse {
parameters: par_out,
value: min,
function_count: fncount,
gradient_count: grcount,
convergence: true,
})
}
}
const BIG: f64 = 1.0e35;
const E1: f64 = 1.7182818;
pub fn samin(
n: usize,
pb: &mut [f64],
min_func: &dyn Fn(Vec<f64>) -> f64,
gen_func_opt: Option<&dyn Fn(Vec<f64>) -> Vec<f64>>,
maxit: usize,
tmax: usize,
parscale: &[f64],
fnscale: f64,
ti: f64,
rng: &mut impl RNG,
) -> f64 {
if n == 0 {
fminfn(n, pb, parscale, fnscale, min_func)
} else {
let mut p = vec![0.0; n];
let mut ptry = vec![0.0; n];
let mut yb: f64 = fminfn(n, pb, parscale, fnscale, min_func);
if !yb.is_finite() {
yb = BIG;
}
p[..n].copy_from_slice(&pb[..n]);
let mut y = yb;
let scale = 1.0 / ti;
let mut its = 1;
while its < maxit {
let t = ti / (its as f64 + E1).ln(); let mut k = 1;
while (k <= tmax) && (its < maxit) {
genptry(n, &p, &mut ptry, scale * t, parscale, rng, gen_func_opt);
let mut ytry: f64 = fminfn(n, &ptry, parscale, fnscale, min_func);
if !ytry.is_finite() {
ytry = BIG;
}
let dy = ytry - y;
if dy <= 0.0 || (rng.unif_rand() < (-dy / t).exp()) {
p[..n].copy_from_slice(&ptry[..n]);
y = ytry; if y <= yb {
pb[..n].copy_from_slice(&p[..n]);
yb = y;
}
}
its += 1;
k += 1;
}
}
yb
}
}
fn genptry(
n: usize,
p: &[f64],
ptry: &mut [f64],
scale: f64,
parscale: &[f64],
rng: &mut impl RNG,
gen_func_opt: Option<&dyn Fn(Vec<f64>) -> Vec<f64>>,
) {
if let Some(gen_func) = gen_func_opt {
let mut x = vec![0.0; n];
for i in 0..n {
x[i] = p[i] * parscale[i];
}
let s = gen_func(x);
for i in 0..n {
ptry[i] = s[i] / parscale[i];
}
} else {
for i in 0..n {
ptry[i] = p[i] + scale * rng.norm_rand(); }
}
}