use crate::core::executor::OptimizationResult;
use crate::core::inner::{InnerExecutor, WarmStart};
use crate::core::math::{
ComponentMulAssign, MatTransposeVec, MatVec, MatrixFromDiagonal, MatrixIdentity, NormSquared,
RankOneUpdate, SampleStandardNormal, ScaleInPlace, ScaledAdd, SymmetricEigen, VectorLen,
};
use crate::core::problem::{CostFunction, Problem};
use crate::core::solver::Solver;
use crate::core::state::{
BasicPopulationState, BasicSimplexState, BasicState, CountsMirror, IntoInitialSimplex,
LbfgsState, State,
};
use crate::core::termination::{TerminationCriterion, TerminationReason};
use crate::solver::cma_es::{sort_population_ascending, CmaEs};
use crate::solver::lbfgs::{Lbfgs, Lbfgsb};
use crate::solver::levenberg_marquardt::LevenbergMarquardt;
use crate::solver::nelder_mead::NelderMead;
pub trait MemeticInner<V>: WarmStart<V> {
fn seed_scaled(&self, x: &V, _sigma: f64) -> Self::State {
self.seed(x)
}
}
type ClosureSeedFn<V, S> = Box<dyn Fn(&V, f64) -> S>;
pub struct ClosureInner<I, S, V> {
inner: I,
seed_fn: ClosureSeedFn<V, S>,
}
impl<I, S, V> ClosureInner<I, S, V> {
pub fn new(inner: I, seed_fn: impl Fn(&V, f64) -> S + 'static) -> Self {
Self {
inner,
seed_fn: Box::new(seed_fn),
}
}
}
impl<P, I, S, V> Solver<P, S> for ClosureInner<I, S, V>
where
I: Solver<P, S>,
S: State,
{
type Error = I::Error;
fn init(&mut self, problem: &mut Problem<P>, state: S) -> Result<S, Self::Error> {
self.inner.init(problem, state)
}
fn next_iter(
&mut self,
problem: &mut Problem<P>,
state: S,
) -> Result<(S, Option<TerminationReason>), Self::Error> {
self.inner.next_iter(problem, state)
}
fn terminate(&self, state: &S) -> Option<TerminationReason> {
self.inner.terminate(state)
}
}
impl<I, S, V> WarmStart<V> for ClosureInner<I, S, V>
where
S: State<Param = V>,
{
type State = S;
fn seed(&self, x: &V) -> S {
(self.seed_fn)(x, 0.0)
}
}
impl<I, S, V> MemeticInner<V> for ClosureInner<I, S, V>
where
S: State<Param = V>,
{
fn seed_scaled(&self, x: &V, sigma: f64) -> S {
(self.seed_fn)(x, sigma)
}
}
impl<V> WarmStart<V> for NelderMead
where
V: VectorLen + Clone + IntoInitialSimplex<V> + std::ops::IndexMut<usize, Output = f64>,
{
type State = BasicSimplexState<V>;
fn seed(&self, x: &V) -> BasicSimplexState<V> {
BasicSimplexState::new(x.clone())
}
}
impl<V> MemeticInner<V> for NelderMead
where
V: VectorLen + Clone + IntoInitialSimplex<V> + std::ops::IndexMut<usize, Output = f64>,
{
fn seed_scaled(&self, x: &V, sigma: f64) -> BasicSimplexState<V> {
let n = x.vec_len();
let mut vertices = Vec::with_capacity(n + 1);
vertices.push(x.clone());
for j in 0..n {
let mut v = x.clone();
v[j] += sigma;
vertices.push(v);
}
BasicSimplexState::from_simplex(vertices)
}
}
impl<V, M> WarmStart<V> for LevenbergMarquardt<V, M>
where
V: Clone,
{
type State = BasicState<V>;
fn seed(&self, x: &V) -> BasicState<V> {
BasicState::new(x.clone())
}
}
impl<V, M> MemeticInner<V> for LevenbergMarquardt<V, M>
where
V: Clone,
{
}
impl<Mode, S, V> WarmStart<V> for Lbfgs<Mode, S>
where
V: Clone,
{
type State = LbfgsState<V>;
fn seed(&self, x: &V) -> LbfgsState<V> {
LbfgsState::new(x.clone(), self.m_capacity)
}
}
impl<V, LS> MemeticInner<V> for Lbfgsb<LS>
where
V: Clone,
{
}
pub struct CmaInject<I, V, M>
where
I: MemeticInner<V>,
{
cma: CmaEs<V, M>,
inner: InnerExecutor<I::State, I>,
k: usize,
c_y_override: Option<f64>,
}
impl<I, V, M> CmaInject<I, V, M>
where
I: MemeticInner<V>,
I::State: CountsMirror,
{
pub fn with_inner_solver(cma: CmaEs<V, M>, inner: I) -> Self {
Self {
cma,
inner: InnerExecutor::new(inner).max_iter(50),
k: 1,
c_y_override: None,
}
}
pub fn with_k(mut self, k: usize) -> Self {
assert!(k >= 1, "CmaInject requires k >= 1, got {}", k);
self.k = k;
self
}
pub fn with_c_y(mut self, c_y: f64) -> Self {
assert!(c_y > 0.0, "CmaInject requires c_y > 0, got {}", c_y);
self.c_y_override = Some(c_y);
self
}
pub fn with_inner_max_iter(self, n: u64) -> Self {
let Self {
cma,
inner,
k,
c_y_override,
} = self;
Self {
cma,
inner: inner.max_iter(n),
k,
c_y_override,
}
}
pub fn inner_terminate_on<C>(self, criterion: C) -> Self
where
C: TerminationCriterion<I::State> + 'static,
{
let Self {
cma,
inner,
k,
c_y_override,
} = self;
Self {
cma,
inner: inner.terminate_on(criterion),
k,
c_y_override,
}
}
}
pub(crate) fn default_c_y(n: usize) -> f64 {
let n = n as f64;
n.sqrt() + 2.0 * n / (n + 2.0)
}
impl<P, I, V, M> Solver<P, BasicPopulationState<V>> for CmaInject<I, V, M>
where
P: CostFunction<Param = V, Output = f64>,
I: MemeticInner<V> + Solver<P, <I as WarmStart<V>>::State, Error = P::Error>,
I::State: State<Param = V, Float = f64> + CountsMirror,
V: VectorLen
+ Clone
+ ScaledAdd<f64>
+ ScaleInPlace
+ ComponentMulAssign
+ NormSquared
+ SampleStandardNormal
+ std::ops::Index<usize, Output = f64>
+ std::ops::IndexMut<usize, Output = f64>,
M: MatrixIdentity
+ MatrixFromDiagonal<V>
+ MatVec<V>
+ MatTransposeVec<V>
+ ScaleInPlace
+ RankOneUpdate<V>
+ SymmetricEigen<V>
+ Clone,
CmaEs<V, M>: Solver<P, BasicPopulationState<V>, Error = P::Error>,
{
type Error = P::Error;
fn init(
&mut self,
problem: &mut Problem<P>,
state: BasicPopulationState<V>,
) -> Result<BasicPopulationState<V>, Self::Error> {
self.cma.init(problem, state)
}
fn next_iter(
&mut self,
problem: &mut Problem<P>,
state: BasicPopulationState<V>,
) -> Result<(BasicPopulationState<V>, Option<TerminationReason>), Self::Error> {
let (mut state, reason) = self.cma.next_iter(problem, state)?;
if let Some(r) = reason {
return Ok((state, Some(r)));
}
let (n, m, sigma) = {
let w = self
.cma
.working()
.expect("CmaEs::init must run before CmaInject::next_iter");
(w.n, w.m.clone(), w.sigma)
};
let c_y = self.c_y_override.unwrap_or_else(|| default_c_y(n));
let refine = self.k.min(state.candidates.len());
for i in 0..refine {
let inner_state = self.inner.solver().seed_scaled(&state.candidates[i], sigma);
let inner_result: OptimizationResult<I::State> =
self.inner.run(problem, inner_state)?;
if inner_result.reason.is_failure() {
return Ok((state, Some(inner_result.reason)));
}
let x_refined = inner_result.state.param().clone();
let mut y = x_refined;
y.scaled_add(-1.0, &m);
y.scale_in_place(1.0 / sigma);
let inv_sqrt_norm = {
let w = self.cma.working().expect("working still populated");
let mut bt_y = w.b.mat_transpose_vec(&y);
bt_y.component_mul_assign(&w.d_inv);
bt_y.norm_squared().sqrt()
};
if inv_sqrt_norm > 0.0 {
let alpha = (c_y / inv_sqrt_norm).min(1.0);
if alpha < 1.0 {
y.scale_in_place(alpha);
}
}
let mut x_inj = m.clone();
x_inj.scaled_add(sigma, &y);
let cost_new = problem.cost(&x_inj)?;
state.candidates[i] = x_inj;
state.costs[i] = cost_new;
}
if refine > 0 {
sort_population_ascending(&mut state.candidates, &mut state.costs);
}
Ok((state, None))
}
fn terminate(&self, state: &BasicPopulationState<V>) -> Option<TerminationReason> {
<CmaEs<V, M> as Solver<P, BasicPopulationState<V>>>::terminate(&self.cma, state)
}
}